diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fd62629..33764fb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added +- Add prop 'isOpenOnMount' to allow opening submenu on mount + ## [2.29.0] - 2021-03-08 ### Added diff --git a/docs/README.md b/docs/README.md index d4b2b7ff..2bc8bfdb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -142,6 +142,7 @@ The available `menu-item` block props are as follows: | `highlight` | `boolean` | Whether the item has highlight | `undefined` | | `iconPosition` | `String` | Icon position relative to the menu item text. Either to the `left` or `right` | `left` | | `iconProps` | `IconProps` | Icon props | `undefined` | +| `isOpenOnMount` | `boolean` | Whether the submenu should always be automatically displayed when its parent is hovered/clicked on (`true`) or not (`false`). | `false` | | `itemProps` | `CategoryItem` or `CustomItem` | Item props | `undefined` | | `classes` | `CustomCSSClasses` | Used to override default CSS handles. To better understand how this prop works, we recommend reading about it [here](https://github.com/vtex-apps/css-handles#usecustomclasses). Note that this is only useful if you're importing this block as a React component. | `undefined` | diff --git a/messages/context.json b/messages/context.json index 9e5c3fd5..21b5b006 100644 --- a/messages/context.json +++ b/messages/context.json @@ -36,6 +36,7 @@ "admin/editor.menu.item.iconId.title": "iconId title", "admin/editor.menu.item.highlight.title": "highlight title", "admin/editor.menu.item.submenuWidth.title": "subMenuWidth title", + "admin/editor.menu.item.isOpenOnMount.title": "isOpenOnMount title", "admin/editor.menu.item.params.title": "params Title", "admin/editor.menu.item.params.categoryId.title": "Category id title", "admin/editor.menu.item.params.text.title": "params text title", @@ -52,4 +53,4 @@ "admin/editor.menu.def.title": "Title value used in radio button", "admin/editor.menu.def.category": "Category value used in radio button", "admin/editor.menu.categoryId.title": "Category ID title" -} \ No newline at end of file +} diff --git a/messages/en.json b/messages/en.json index 3b23be48..bc47b948 100644 --- a/messages/en.json +++ b/messages/en.json @@ -36,6 +36,7 @@ "admin/editor.menu.item.iconId.title": "Icon ID", "admin/editor.menu.item.highlight.title": "Highlight", "admin/editor.menu.item.submenuWidth.title": "Submenu Width", + "admin/editor.menu.item.isOpenOnMount.title": "Is Submenu open on mount", "admin/editor.menu.item.params.title": "Params", "admin/editor.menu.item.params.categoryId.title": "Category ID", "admin/editor.menu.item.params.text.title": "Text", @@ -52,4 +53,4 @@ "admin/editor.menu.def.title": "Title", "admin/editor.menu.def.category": "Category", "admin/editor.menu.categoryId.title": "Category ID" -} \ No newline at end of file +} diff --git a/messages/es.json b/messages/es.json index 9b555a70..959b0f7f 100644 --- a/messages/es.json +++ b/messages/es.json @@ -36,6 +36,7 @@ "admin/editor.menu.item.iconId.title": "ID de icono", "admin/editor.menu.item.highlight.title": "Destacado", "admin/editor.menu.item.submenuWidth.title": "Ancho del submenú", + "admin/editor.menu.item.isOpenOnMount.title": "Submenú abierto on mount", "admin/editor.menu.item.params.title": "Parámetros", "admin/editor.menu.item.params.categoryId.title": "ID de la categoría", "admin/editor.menu.item.params.text.title": "Texto", diff --git a/messages/ja-JP.json b/messages/ja-JP.json index ebd3bd47..924c1ca2 100644 --- a/messages/ja-JP.json +++ b/messages/ja-JP.json @@ -33,6 +33,7 @@ "admin/editor.menu.item.iconId.title": "アイコン ID", "admin/editor.menu.item.highlight.title": "ハイライト", "admin/editor.menu.item.submenuWidth.title": "サブメニュー幅", + "admin/editor.menu.item.isOpenOnMount.title": "マウント時にサブメニューが開いているか", "admin/editor.menu.item.params.title": "パラメータ", "admin/editor.menu.item.params.categoryId.title": "カテゴリ ID", "admin/editor.menu.item.params.text.title": "テキスト", @@ -48,4 +49,4 @@ "admin/editor.menu.def.title": "タイトル", "admin/editor.menu.def.category": "カテゴリ", "admin/editor.menu.categoryId.title": "カテゴリ ID" -} \ No newline at end of file +} diff --git a/messages/pt.json b/messages/pt.json index 0592da6a..9f4a3bc6 100644 --- a/messages/pt.json +++ b/messages/pt.json @@ -36,6 +36,7 @@ "admin/editor.menu.item.iconId.title": "ID do ícone", "admin/editor.menu.item.highlight.title": "Destaque", "admin/editor.menu.item.submenuWidth.title": "Largura do Submenu", + "admin/editor.menu.item.isOpenOnMount.title": "Submenu aberto on mount", "admin/editor.menu.item.params.title": "Parâmetros", "admin/editor.menu.item.params.categoryId.title": "ID da Categoria", "admin/editor.menu.item.params.text.title": "Texto", @@ -52,4 +53,4 @@ "admin/editor.menu.def.title": "Título", "admin/editor.menu.def.category": "Categoria", "admin/editor.menu.categoryId.title": "Id da Categoria" -} \ No newline at end of file +} diff --git a/react/MenuItem.tsx b/react/MenuItem.tsx index 5465aa5a..98b8ecff 100644 --- a/react/MenuItem.tsx +++ b/react/MenuItem.tsx @@ -33,16 +33,19 @@ export interface MenuItemSchema { blockClass?: string experimentalOptimizeRendering?: boolean classes?: CssHandlesTypes.CustomClasses + isOpenOnMount?: boolean } -const submenuInitialState = { - hasBeenActive: false, - isActive: false, +type SubmenuState = { + hasBeenActive: boolean + isActive: boolean + isOpenOnMount?: boolean } -type SubmenuState = typeof submenuInitialState - -type SubmenuAction = { type: 'SHOW_SUBMENU' } | { type: 'HIDE_SUBMENU' } +type SubmenuAction = + | { type: 'SHOW_SUBMENU' } + | { type: 'HIDE_SUBMENU' } + | { type: 'DISABLE_IS_OPEN_ON_MOUNT_FLAG' } const submenuReducer: Reducer = ( state, @@ -59,6 +62,11 @@ const submenuReducer: Reducer = ( ...state, isActive: false, } + case 'DISABLE_IS_OPEN_ON_MOUNT_FLAG': + return { + ...state, + isOpenOnMount: false, + } default: return state } @@ -66,13 +74,18 @@ const submenuReducer: Reducer = ( const MenuItem: StorefrontFunctionComponent = ({ children, + isOpenOnMount = false, ...props }) => { const { experimentalOptimizeRendering } = useContext(MenuContext) - const [{ isActive, hasBeenActive }, dispatch] = useReducer( - submenuReducer, - submenuInitialState - ) + const [ + { isActive, hasBeenActive, isOpenOnMount: isOpenOnMountFlag }, + dispatch, + ] = useReducer(submenuReducer, { + hasBeenActive: isOpenOnMount, + isActive: isOpenOnMount, + isOpenOnMount, + }) const [isHovered, setHovered] = useState(false) const setActive = useCallback( (value: boolean) => { @@ -83,6 +96,12 @@ const MenuItem: StorefrontFunctionComponent = ({ [isActive] ) + const disableIsOpenOnMountFlag = useCallback(() => { + if (isOpenOnMountFlag) { + dispatch({ type: 'DISABLE_IS_OPEN_ON_MOUNT_FLAG' }) + } + }, [isOpenOnMountFlag]) + // Close any active/open menu when url changes useUrlChange(() => { if (isActive) setActive(false) @@ -114,13 +133,13 @@ const MenuItem: StorefrontFunctionComponent = ({ } // if a menu is still active but is not hovered for at least 400ms, close it - if (isActive && !isHovered) { + if (isActive && !isHovered && !isOpenOnMountFlag) { closeTimeout.current = window.setTimeout(() => { setActive(false) }, 400) } }, - [isActive, isCollapsible, isHovered, setActive] + [isActive, isCollapsible, isHovered, setActive, isOpenOnMountFlag] ) const { handles } = useCssHandles(CSS_HANDLES, { classes: props.classes }) @@ -156,6 +175,7 @@ const MenuItem: StorefrontFunctionComponent = ({ onMouseEnter={() => { debouncedSetActive(true) setHovered(true) + disableIsOpenOnMountFlag() }} onMouseLeave={() => { debouncedSetActive(false) diff --git a/store/contentSchemas.json b/store/contentSchemas.json index 14c93091..0ff41d89 100644 --- a/store/contentSchemas.json +++ b/store/contentSchemas.json @@ -8,9 +8,7 @@ { "properties": { "additionalDef": { - "enum": [ - "category" - ] + "enum": ["category"] }, "categoryId": { "type": "integer", @@ -21,9 +19,7 @@ { "properties": { "additionalDef": { - "enum": [ - "title" - ] + "enum": ["title"] }, "title": { "$ref": "app:vtex.menu#/definitions/MenuItem" @@ -33,9 +29,7 @@ { "properties": { "additionalDef": { - "enum": [ - "enum" - ] + "enum": ["enum"] } } } @@ -45,11 +39,7 @@ "properties": { "additionalDef": { "title": "admin/editor.menu.additionalDef.title", - "enum": [ - "none", - "category", - "title" - ], + "enum": ["none", "category", "title"], "enumNames": [ "admin/editor.menu.def.none", "admin/editor.menu.def.category", @@ -88,10 +78,7 @@ "orientation": { "title": "admin/editor.menu.orientation.title", "type": "string", - "enum": [ - "vertical", - "horizontal" - ], + "enum": ["vertical", "horizontal"], "enumNames": [ "admin/editor.menu.orientation.vertical.label", "admin/editor.menu.orientation.horizontal.label" @@ -115,9 +102,7 @@ { "properties": { "type": { - "enum": [ - "category" - ] + "enum": ["category"] }, "itemProps": { "type": "object", @@ -133,9 +118,7 @@ { "properties": { "type": { - "enum": [ - "custom" - ] + "enum": ["custom"] }, "itemProps": { "type": "object", @@ -143,10 +126,7 @@ "type": { "title": "admin/editor.menu.item.params.type.title", "type": "string", - "enum": [ - "internal", - "external" - ], + "enum": ["internal", "external"], "enumNames": [ "admin/editor.menu.item.params.internal.title", "admin/editor.menu.item.params.external.title" @@ -183,10 +163,7 @@ "type": { "title": "admin/editor.menu.item.type.title", "type": "string", - "enum": [ - "category", - "custom" - ], + "enum": ["category", "custom"], "default": "custom", "enumNames": [ "admin/editor.menu.item.category.title", @@ -200,6 +177,11 @@ "title": "admin/editor.menu.item.iconId.title", "type": "string", "nullable": true + }, + "isOpenOnMount": { + "title": "admin/editor.menu.item.isOpenOnMount.title", + "type": "boolean", + "nullable": true } } }, @@ -208,13 +190,10 @@ "properties": { "submenuWidth": { "title": "admin/editor.menu.item.submenuWidth.title", - "enum": [ - "100%", - "auto" - ], + "enum": ["100%", "auto"], "default": "auto" } } } } -} \ No newline at end of file +}