From 897c8ea5fc18a0e1691cb0cf14270edf952f5eb7 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 22 Mar 2020 21:03:17 +0000 Subject: [PATCH] Dashboard: RTL support and dynamic story creation URL (#696) * Include dashboard in code coverage reports * Lint fixes * Rename variable for stories editor config * Pass server-side config to dashboard app * Update button to create new story, add RTL support --- .../dashboard/app/config/configProvider.js | 39 +++++++++++++++++ assets/src/dashboard/app/config/context.js | 22 ++++++++++ assets/src/dashboard/app/config/index.js | 18 ++++++++ assets/src/dashboard/app/config/useConfig.js | 31 +++++++++++++ assets/src/dashboard/app/index.js | 43 +++++++++++++------ .../src/dashboard/components/button/index.js | 2 + .../dashboard/components/navigation-bar.js | 16 ++++--- assets/src/dashboard/constants.js | 2 - assets/src/dashboard/index.js | 20 ++++++--- assets/src/edit-story/app/config/context.js | 2 +- assets/src/edit-story/index.js | 2 +- includes/Dashboard.php | 19 ++++++++ includes/Story_Post_Type.php | 8 ++-- tests/js/jest.config.js | 1 + tests/js/setup-globals.js | 3 +- 15 files changed, 195 insertions(+), 33 deletions(-) create mode 100644 assets/src/dashboard/app/config/configProvider.js create mode 100644 assets/src/dashboard/app/config/context.js create mode 100644 assets/src/dashboard/app/config/index.js create mode 100644 assets/src/dashboard/app/config/useConfig.js diff --git a/assets/src/dashboard/app/config/configProvider.js b/assets/src/dashboard/app/config/configProvider.js new file mode 100644 index 000000000000..2510a62f1564 --- /dev/null +++ b/assets/src/dashboard/app/config/configProvider.js @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * External dependencies + */ +import PropTypes from 'prop-types'; + +/** + * Internal dependencies + */ +import Context from './context'; + +function ConfigProvider({ config, children }) { + return {children}; +} + +ConfigProvider.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + ]).isRequired, + config: PropTypes.object.isRequired, +}; + +export default ConfigProvider; diff --git a/assets/src/dashboard/app/config/context.js b/assets/src/dashboard/app/config/context.js new file mode 100644 index 000000000000..95308ecabfb3 --- /dev/null +++ b/assets/src/dashboard/app/config/context.js @@ -0,0 +1,22 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * External dependencies + */ +import { createContext } from 'react'; + +export default createContext({ api: {} }); diff --git a/assets/src/dashboard/app/config/index.js b/assets/src/dashboard/app/config/index.js new file mode 100644 index 000000000000..34ab88aa0363 --- /dev/null +++ b/assets/src/dashboard/app/config/index.js @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { default as ConfigProvider } from './configProvider'; +export { default as useConfig } from './useConfig'; diff --git a/assets/src/dashboard/app/config/useConfig.js b/assets/src/dashboard/app/config/useConfig.js new file mode 100644 index 000000000000..f77f0d81973e --- /dev/null +++ b/assets/src/dashboard/app/config/useConfig.js @@ -0,0 +1,31 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * External dependencies + */ +import { useContext } from 'react'; + +/** + * Internal dependencies + */ +import Context from './context'; + +function useConfig() { + return useContext(Context); +} + +export default useConfig; diff --git a/assets/src/dashboard/app/index.js b/assets/src/dashboard/app/index.js index be206fbb1d6b..c46de87eebfc 100644 --- a/assets/src/dashboard/app/index.js +++ b/assets/src/dashboard/app/index.js @@ -17,7 +17,9 @@ /** * External dependencies */ -import { ThemeProvider } from 'styled-components'; +import { StyleSheetManager, ThemeProvider } from 'styled-components'; +import stylisRTLPlugin from 'stylis-plugin-rtl'; +import PropTypes from 'prop-types'; /** * Internal dependencies @@ -25,22 +27,37 @@ import { ThemeProvider } from 'styled-components'; import theme, { GlobalStyle } from '../theme'; import KeyboardOnlyOutline from '../utils/keyboardOnlyOutline'; import { NavigationBar } from '../components'; -import { Route, RouterProvider } from './router'; +import { useRouteHistory, Route, RouterProvider } from './router'; +import { useConfig, ConfigProvider } from './config'; import { MyStoriesView, TemplatesGalleryView, MyBookmarksView } from './views'; -function App() { +function App({ config }) { + const { isRTL } = config; return ( - - - - - - } /> - } /> - } /> - - + + + + + + + + } /> + } + /> + } /> + + + + ); } +App.propTypes = { + config: PropTypes.object.isRequired, +}; + export default App; + +export { useConfig, useRouteHistory }; diff --git a/assets/src/dashboard/components/button/index.js b/assets/src/dashboard/components/button/index.js index 7055ca1351ee..a05696902d97 100644 --- a/assets/src/dashboard/components/button/index.js +++ b/assets/src/dashboard/components/button/index.js @@ -36,12 +36,14 @@ const StyledButton = styled.button` min-width: 132px; opacity: 0.75; padding: 0; + text-decoration: none; &:focus, &:active, &:hover { opacity: 1; outline: none; + color: ${({ theme }) => theme.colors.white}; } ${KEYBOARD_USER_SELECTOR} &:focus { diff --git a/assets/src/dashboard/components/navigation-bar.js b/assets/src/dashboard/components/navigation-bar.js index 41fe98594eb5..fba205ea1348 100644 --- a/assets/src/dashboard/components/navigation-bar.js +++ b/assets/src/dashboard/components/navigation-bar.js @@ -27,9 +27,9 @@ import styled from 'styled-components'; /** * Internal dependencies */ -import { useRouteHistory } from '../app/router'; +import { useConfig, useRouteHistory } from '../app'; import { ReactComponent as WebStoriesLogoSVG } from '../images/logo.svg'; -import { BUTTON_TYPES, NEW_STORY_URL } from '../constants'; +import { BUTTON_TYPES } from '../constants'; import Button from './button'; import Dropdown from './dropdown'; @@ -82,6 +82,10 @@ const LinksContainer = styled.div` } `; +const NewStoryLink = styled(Button)` + margin-left: 40px; +`; + const paths = [ { value: '/', label: __('My Stories', 'web-stories') }, { @@ -93,6 +97,7 @@ const paths = [ function NavigationBar() { const { state, actions } = useRouteHistory(); + const { newStoryURL } = useConfig(); return ( ); diff --git a/assets/src/dashboard/constants.js b/assets/src/dashboard/constants.js index 1cb9879d88ad..bb1e525a216f 100644 --- a/assets/src/dashboard/constants.js +++ b/assets/src/dashboard/constants.js @@ -32,5 +32,3 @@ export const KEYBOARD_USER_SELECTOR = `.${KEYBOARD_USER_CLASS}`; export const Z_INDEX = { POPOVER_MENU: 10, }; - -export const NEW_STORY_URL = 'post-new.php?post_type=web-story'; diff --git a/assets/src/dashboard/index.js b/assets/src/dashboard/index.js index 198dade7036e..8130dd6d214b 100644 --- a/assets/src/dashboard/index.js +++ b/assets/src/dashboard/index.js @@ -26,16 +26,24 @@ import App from './app'; import './style.css'; // This way the general dashboard styles are loaded before all the component styles. /** - * Initializes the dashboard screen. + * Initializes the Web Stories dashboard screen. + * + * @param {string} id ID of the root element to render the screen in. + * @param {Object} config Story editor settings. */ -const initialize = () => { - const appElement = document.getElementById('web-stories-dashboard'); +const initialize = (id, config) => { + const appElement = document.getElementById(id); + + render(, appElement); +}; - render(, appElement); +const initializeWithConfig = () => { + const { id, config } = window.webStoriesDashboardSettings; + initialize(id, config); }; if ('loading' === document.readyState) { - document.addEventListener('DOMContentLoaded', initialize); + document.addEventListener('DOMContentLoaded', initializeWithConfig); } else { - initialize(); + initializeWithConfig(); } diff --git a/assets/src/edit-story/app/config/context.js b/assets/src/edit-story/app/config/context.js index 95308ecabfb3..fecf09d7b9f2 100644 --- a/assets/src/edit-story/app/config/context.js +++ b/assets/src/edit-story/app/config/context.js @@ -19,4 +19,4 @@ */ import { createContext } from 'react'; -export default createContext({ api: {} }); +export default createContext({}); diff --git a/assets/src/edit-story/index.js b/assets/src/edit-story/index.js index 5f0e0adc0710..f40e1bffea9a 100644 --- a/assets/src/edit-story/index.js +++ b/assets/src/edit-story/index.js @@ -42,7 +42,7 @@ const initialize = (id, config) => { }; const initializeWithConfig = () => { - const { id, config } = window.ampStoriesEditSettings; + const { id, config } = window.webStoriesEditorSettings; initialize(id, config); }; diff --git a/includes/Dashboard.php b/includes/Dashboard.php index dac168cccefe..0362afbbb779 100644 --- a/includes/Dashboard.php +++ b/includes/Dashboard.php @@ -122,6 +122,25 @@ public function enqueue_assets( $hook_suffix ) { wp_set_script_translations( self::SCRIPT_HANDLE, 'web-stories' ); + $new_story_url = admin_url( add_query_arg( + [ + 'post_type' => Story_Post_Type::POST_TYPE_SLUG, + ], + 'post-new.php' + ) ); + + wp_localize_script( + self::SCRIPT_HANDLE, + 'webStoriesDashboardSettings', + [ + 'id' => 'web-stories-dashboard', + 'config' => [ + 'isRTL' => is_rtl(), + 'newStoryURL' => $new_story_url, + ], + ] + ); + wp_register_style( 'google-sans-font', 'https://fonts.googleapis.com/css?family=Google+Sans|Google+Sans:b', diff --git a/includes/Story_Post_Type.php b/includes/Story_Post_Type.php index a16129cf396d..0356d0da017a 100644 --- a/includes/Story_Post_Type.php +++ b/includes/Story_Post_Type.php @@ -35,21 +35,21 @@ */ class Story_Post_Type { /** - * The slug of the post type to store URLs that have AMP errors. + * The slug of the stories post type. * * @var string */ const POST_TYPE_SLUG = 'web-story'; /** - * AMP Stories script handle. + * Web Stories editor script handle. * * @var string */ const WEB_STORIES_SCRIPT_HANDLE = 'edit-story'; /** - * AMP Stories style handle. + * Web Stories editor style handle. * * @var string */ @@ -304,7 +304,7 @@ public static function admin_enqueue_scripts( $hook ) { wp_localize_script( self::WEB_STORIES_SCRIPT_HANDLE, - 'ampStoriesEditSettings', + 'webStoriesEditorSettings', [ 'id' => 'edit-story', 'config' => [ diff --git a/tests/js/jest.config.js b/tests/js/jest.config.js index c4da7db94a99..10fda7b43268 100644 --- a/tests/js/jest.config.js +++ b/tests/js/jest.config.js @@ -46,6 +46,7 @@ module.exports = { coverageDirectory: '/build/logs', collectCoverageFrom: [ '/assets/src/edit-story/**/*.js', + '/assets/src/dashboard/**/*.js', '!**/test/**', '!**/stories/**', ], diff --git a/tests/js/setup-globals.js b/tests/js/setup-globals.js index 23e1d2ae5f11..675acdee5695 100644 --- a/tests/js/setup-globals.js +++ b/tests/js/setup-globals.js @@ -14,7 +14,8 @@ * limitations under the License. */ -window.ampStoriesEditSettings = {}; +window.webStoriesEditorSettings = {}; +window.webStoriesDashboardSettings = {}; global.wp = { media: {