From b5dc3a6983a220d175ee9326325d8cff6a25de31 Mon Sep 17 00:00:00 2001 From: cchaos Date: Thu, 19 Nov 2020 17:14:44 -0500 Subject: [PATCH 01/12] [EuiKeyPadMenuItem] Added `isSelected` and fixed a few interactive and a11y props/elements --- .../key_pad_menu/key_pad_menu_item_button.js | 13 +- .../key_pad_menu_item.test.tsx.snap | 230 ++++++++++++++++-- src/components/key_pad_menu/_index.scss | 1 + .../key_pad_menu/_key_pad_menu.scss | 100 -------- .../key_pad_menu/_key_pad_menu_item.scss | 105 ++++++++ .../key_pad_menu/key_pad_menu_item.test.tsx | 90 ++++++- .../key_pad_menu/key_pad_menu_item.tsx | 67 +++-- 7 files changed, 466 insertions(+), 140 deletions(-) create mode 100644 src/components/key_pad_menu/_key_pad_menu_item.scss diff --git a/src-docs/src/views/key_pad_menu/key_pad_menu_item_button.js b/src-docs/src/views/key_pad_menu/key_pad_menu_item_button.js index 78885f342cf..1994b39e07a 100644 --- a/src-docs/src/views/key_pad_menu/key_pad_menu_item_button.js +++ b/src-docs/src/views/key_pad_menu/key_pad_menu_item_button.js @@ -8,10 +8,19 @@ import { export default () => ( - {}}> + {}}> - {}}> + + + + + + + + + + diff --git a/src/components/key_pad_menu/__snapshots__/key_pad_menu_item.test.tsx.snap b/src/components/key_pad_menu/__snapshots__/key_pad_menu_item.test.tsx.snap index e7844442e97..03e4d1fd345 100644 --- a/src/components/key_pad_menu/__snapshots__/key_pad_menu_item.test.tsx.snap +++ b/src/components/key_pad_menu/__snapshots__/key_pad_menu_item.test.tsx.snap @@ -1,49 +1,245 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`EuiKeyPadMenuItem is rendered 1`] = ` - + + + Icon + +

+ Label +

+
+ +`; + +exports[`EuiKeyPadMenuItem props betaBadge renders 1`] = ` + +`; + +exports[`EuiKeyPadMenuItem props betaBadge renders with betaBadgeIconType 1`] = ` + +`; + +exports[`EuiKeyPadMenuItem props betaBadge renders with betaBadgeTooltipContent 1`] = ` + +`; + +exports[`EuiKeyPadMenuItem props isDisabled renders with href 1`] = ` + +`; + +exports[`EuiKeyPadMenuItem props isDisabled renders with onClick 1`] = ` + +`; + +exports[`EuiKeyPadMenuItem props isSelected renders with href 1`] = ` +
-
-
Icon -
+

Label

-
+
`; +exports[`EuiKeyPadMenuItem props isSelected renders with onClick 1`] = ` + +`; + exports[`EuiKeyPadMenuItem renders button 1`] = ` `; @@ -53,20 +249,20 @@ exports[`EuiKeyPadMenuItem renders href 1`] = ` href="#" rel="noreferrer" > -
-
Icon -
+

Label

-
+ `; @@ -76,19 +272,19 @@ exports[`EuiKeyPadMenuItem renders href with rel 1`] = ` href="#" rel="noreferrer" > -
-
Icon -
+

Label

-
+ `; diff --git a/src/components/key_pad_menu/_index.scss b/src/components/key_pad_menu/_index.scss index d3c3e4260d3..4a95eb2c05e 100644 --- a/src/components/key_pad_menu/_index.scss +++ b/src/components/key_pad_menu/_index.scss @@ -3,3 +3,4 @@ @import 'variables'; @import 'key_pad_menu'; +@import 'key_pad_menu_item'; diff --git a/src/components/key_pad_menu/_key_pad_menu.scss b/src/components/key_pad_menu/_key_pad_menu.scss index b8360ed9c5f..258aac1f2f2 100644 --- a/src/components/key_pad_menu/_key_pad_menu.scss +++ b/src/components/key_pad_menu/_key_pad_menu.scss @@ -8,103 +8,3 @@ width: $euiKeyPadMenuSize * 3; max-width: 100%; } - -/** - * 1. If this class is applied to a button, we need to override the Chrome default font. - * 2. If it has a BetaBadge, make sure only the first letter shows - */ -.euiKeyPadMenuItem { - @include euiFont; /* 1 */ - // Disable indentation for transition legibility - // sass-lint:disable-block indentation - display: block; - padding: $euiSizeXS; - height: $euiKeyPadMenuSize; - width: $euiKeyPadMenuSize; - color: $euiColorDarkShade; - border: $euiBorderThin; - border-color: transparent; - border-radius: $euiBorderRadius; - transition: - border-color $euiAnimSpeedFast ease-in, - box-shadow $euiAnimSpeedFast ease-in; - - &:not(:disabled) { - &:hover, - &:focus { - @include euiSlightShadow; - border-color: $euiBorderColor; - - .euiKeyPadMenuItem__icon { // sass-lint:disable-line nesting-depth - transform: translateY(0); - } - } - - } - - &:disabled { - color: $euiButtonColorDisabledText; - cursor: not-allowed; - - .euiKeyPadMenuItem__icon { - filter: grayscale(100%); - - svg * { // sass-lint:disable-line nesting-depth - fill: $euiButtonColorDisabledText; - } - } - - } -} - -.euiKeyPadMenuItem__inner { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - - .euiKeyPadMenuItem--hasBetaBadge & { - position: relative; - - .euiKeyPadMenuItem__betaBadgeWrapper { - position: absolute; - top: $euiSizeXS; - right: $euiSizeS; - z-index: 3; - - // sass-lint:disable-block nesting-depth - .euiKeyPadMenuItem__betaBadge:not(.euiBetaBadge--iconOnly) { - padding: 0 ($euiSizeM / 2); /* 2 */ - overflow: hidden; /* 2 */ - letter-spacing: 3rem; /* 2 */ - } - } - } -} - -.euiKeyPadMenuItem__betaBadge { - @include size($euiKeyPadMenuItemBetaBadgeSize); - line-height: $euiKeyPadMenuItemBetaBadgeSize; - color: $euiColorFullShade; - background-color: tintOrShade($euiColorLightShade, 50%, 0%); - box-shadow: none; - - .euiBetaBadge__icon { - @include size($euiSizeM); - } -} - -.euiKeyPadMenuItem__icon { - transition: transform $euiAnimSpeedNormal $euiAnimSlightBounce; - transform: translateY(2px); - margin-bottom: $euiSizeM; -} - -.euiKeyPadMenuItem__label { - font-size: $euiFontSizeXS; - font-weight: $euiFontWeightMedium; - line-height: $euiSize; - text-align: center; -} diff --git a/src/components/key_pad_menu/_key_pad_menu_item.scss b/src/components/key_pad_menu/_key_pad_menu_item.scss new file mode 100644 index 00000000000..3b698e7dc6f --- /dev/null +++ b/src/components/key_pad_menu/_key_pad_menu_item.scss @@ -0,0 +1,105 @@ +/** + * 1. If this class is applied to a button, we need to override the Chrome default font. + * 2. If it has a BetaBadge, make sure only the first letter shows + */ +.euiKeyPadMenuItem { + @include euiFont; /* 1 */ + // Disable indentation for transition legibility + // sass-lint:disable-block indentation + display: block; + padding: $euiSizeXS; + height: $euiKeyPadMenuSize; + width: $euiKeyPadMenuSize; + color: $euiTextSubduedColor; + border: $euiBorderThin; + border-color: transparent; + border-radius: $euiBorderRadius; + transition: + border-color $euiAnimSpeedFast ease-in, + box-shadow $euiAnimSpeedFast ease-in; + + // Using the aria selectors to ensure proper a11y + &[aria-pressed='true'], + &[aria-current] { + color: $euiTitleColor; + border-color: $euiBorderColor; + } + + &:not(:disabled) { + &:hover, + &:focus { + @include euiSlightShadow; + border-color: $euiBorderColor; + + .euiKeyPadMenuItem__icon { // sass-lint:disable-line nesting-depth + transform: translateY(0); + } + } + + } + + &:disabled { + color: $euiButtonColorDisabledText; + cursor: not-allowed; + + .euiKeyPadMenuItem__icon { + filter: grayscale(100%); + + svg * { // sass-lint:disable-line nesting-depth + fill: $euiButtonColorDisabledText; + } + } + } +} + +.euiKeyPadMenuItem__inner { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + .euiKeyPadMenuItem--hasBetaBadge & { + position: relative; + + .euiKeyPadMenuItem__betaBadgeWrapper { + position: absolute; + top: $euiSizeXS; + right: $euiSizeS; + z-index: 3; + + // sass-lint:disable-block nesting-depth + .euiKeyPadMenuItem__betaBadge:not(.euiBetaBadge--iconOnly) { + padding: 0 ($euiSizeM / 2); /* 2 */ + overflow: hidden; /* 2 */ + letter-spacing: 3rem; /* 2 */ + } + } + } +} + +.euiKeyPadMenuItem__betaBadge { + @include size($euiKeyPadMenuItemBetaBadgeSize); + line-height: $euiKeyPadMenuItemBetaBadgeSize; + color: $euiColorFullShade; + background-color: tintOrShade($euiColorLightShade, 50%, 0%); + box-shadow: none; + + .euiBetaBadge__icon { + @include size($euiSizeM); + } +} + +.euiKeyPadMenuItem__icon { + transition: transform $euiAnimSpeedNormal $euiAnimSlightBounce; + transform: translateY(2px); + margin-bottom: $euiSizeM; +} + +.euiKeyPadMenuItem__label { + font-size: $euiFontSizeXS; + font-weight: $euiFontWeightMedium; + line-height: $euiSize; + text-align: center; +} diff --git a/src/components/key_pad_menu/key_pad_menu_item.test.tsx b/src/components/key_pad_menu/key_pad_menu_item.test.tsx index 3e6f1491816..bf2407f9dca 100644 --- a/src/components/key_pad_menu/key_pad_menu_item.test.tsx +++ b/src/components/key_pad_menu/key_pad_menu_item.test.tsx @@ -26,7 +26,7 @@ import { EuiKeyPadMenuItem } from './key_pad_menu_item'; describe('EuiKeyPadMenuItem', () => { test('is rendered', () => { const component = render( - + Icon ); @@ -34,6 +34,94 @@ describe('EuiKeyPadMenuItem', () => { expect(component).toMatchSnapshot(); }); + describe('props', () => { + describe('isDisabled', () => { + test('renders with href', () => { + const component = render( + + Icon + + ); + + expect(component).toMatchSnapshot(); + }); + + test('renders with onClick', () => { + const onClickHandler = jest.fn(); + + const component = render( + + Icon + + ); + + expect(component).toMatchSnapshot(); + }); + }); + + describe('isSelected', () => { + test('renders with href', () => { + const component = render( + + Icon + + ); + + expect(component).toMatchSnapshot(); + }); + + test('renders with onClick', () => { + const onClickHandler = jest.fn(); + + const component = render( + + Icon + + ); + + expect(component).toMatchSnapshot(); + }); + }); + + describe('betaBadge', () => { + test('renders', () => { + const component = render( + + Icon + + ); + + expect(component).toMatchSnapshot(); + }); + + test('renders with betaBadgeIconType', () => { + const component = render( + + Icon + + ); + + expect(component).toMatchSnapshot(); + }); + + test('renders with betaBadgeTooltipContent', () => { + const component = render( + + Icon + + ); + + expect(component).toMatchSnapshot(); + }); + }); + }); + test('renders href', () => { const component = render( diff --git a/src/components/key_pad_menu/key_pad_menu_item.tsx b/src/components/key_pad_menu/key_pad_menu_item.tsx index b94347a747f..2c9ab45af9f 100644 --- a/src/components/key_pad_menu/key_pad_menu_item.tsx +++ b/src/components/key_pad_menu/key_pad_menu_item.tsx @@ -18,15 +18,19 @@ */ import React, { - AnchorHTMLAttributes, - ButtonHTMLAttributes, FunctionComponent, ReactNode, HTMLAttributes, + Ref, } from 'react'; import classNames from 'classnames'; -import { CommonProps, ExclusiveUnion } from '../common'; +import { + CommonProps, + ExclusiveUnion, + PropsForAnchor, + PropsForButton, +} from '../common'; import { EuiBetaBadge } from '../badge/beta_badge'; @@ -41,7 +45,7 @@ const renderContent = ( betaBadgeTooltipContent?: ReactNode, betaBadgeIconType?: IconType ) => ( -
+ {betaBadgeLabel && ( )} -
{children}
+ {children}

{label}

-
+ ); interface EuiKeyPadMenuItemCommonProps { /** - * ReactNode to render as this component's content + * Pass an EuiIcon, preferrably `size="l"` */ children: ReactNode; isDisabled?: boolean; + /** + * Indicate if an item is the current one. + * Be sure to use `true` AND `false` when acting as a toggle. + */ + isSelected?: boolean; + /** + * The text to display beneath the icon + */ label: ReactNode; - /** * Add a badge to the card to label it as "Beta" or other non-GA state */ betaBadgeLabel?: string; - /** * Supply an icon type if the badge should just be an icon */ betaBadgeIconType?: IconType; - /** * Add a description to the beta badge (will appear in a tooltip) */ betaBadgeTooltipContent?: ReactNode; - onClick?: () => void; - href?: string; rel?: string; } +type EuiKeyPadMenuItemPropsForAnchor = PropsForAnchor< + EuiKeyPadMenuItemCommonProps, + { + buttonRef?: Ref; + } +>; + +type EuiKeyPadMenuItemPropsForButton = PropsForButton< + EuiKeyPadMenuItemCommonProps, + { + buttonRef?: Ref; + } +>; + export type EuiKeyPadMenuItemProps = CommonProps & ExclusiveUnion< - Omit, 'onClick'>, - Omit, 'onClick'> - > & - EuiKeyPadMenuItemCommonProps; + EuiKeyPadMenuItemPropsForAnchor, + EuiKeyPadMenuItemPropsForButton + >; export const EuiKeyPadMenuItem: FunctionComponent = ({ isDisabled, + isSelected, label, children, className, @@ -104,6 +125,7 @@ export const EuiKeyPadMenuItem: FunctionComponent = ({ href, rel, target, + buttonRef, ...rest }) => { const classes = classNames( @@ -116,28 +138,33 @@ export const EuiKeyPadMenuItem: FunctionComponent = ({ const Element = href && !isDisabled ? 'a' : 'button'; const relObj: { - role?: string; disabled?: boolean; type?: string; href?: string; rel?: string; target?: string; + 'aria-pressed'?: boolean; + 'aria-current'?: boolean; } = {}; if (href && !isDisabled) { relObj.href = href; + relObj.rel = getSecureRelForTarget({ href, target, rel }); relObj.target = target; - relObj.rel = getSecureRelForTarget({ href, rel, target }); + relObj['aria-current'] = isSelected ? isSelected : undefined; } else { - relObj.type = 'button'; relObj.disabled = isDisabled; + relObj.type = 'button'; + relObj['aria-pressed'] = isSelected; } return ( )} - {...(rest as HTMLAttributes)}> + {...(rest as HTMLAttributes)} + // ref={buttonRef} HELP! + > {renderContent( children, label, From 5788b047f5a9489a020810feedfad845254dcc80 Mon Sep 17 00:00:00 2001 From: cchaos Date: Thu, 19 Nov 2020 17:39:01 -0500 Subject: [PATCH 02/12] Adding styles for Amsterdam version Also added a helper mixin for `euiAnimationsAllowed` --- .../key_pad_menu/key_pad_menu_item_button.js | 4 +-- .../key_pad_menu/_key_pad_menu_item.scss | 11 +++--- src/global_styling/mixins/_button.scss | 2 +- src/global_styling/mixins/_helpers.scss | 8 +++++ .../eui-amsterdam/overrides/_index.scss | 1 + .../overrides/_key_pad_menu.scss | 34 +++++++++++++++++++ 6 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 src/themes/eui-amsterdam/overrides/_key_pad_menu.scss diff --git a/src-docs/src/views/key_pad_menu/key_pad_menu_item_button.js b/src-docs/src/views/key_pad_menu/key_pad_menu_item_button.js index 1994b39e07a..bbe6be0dd3f 100644 --- a/src-docs/src/views/key_pad_menu/key_pad_menu_item_button.js +++ b/src-docs/src/views/key_pad_menu/key_pad_menu_item_button.js @@ -14,10 +14,10 @@ export default () => ( - + - + diff --git a/src/components/key_pad_menu/_key_pad_menu_item.scss b/src/components/key_pad_menu/_key_pad_menu_item.scss index 3b698e7dc6f..07ff970a41d 100644 --- a/src/components/key_pad_menu/_key_pad_menu_item.scss +++ b/src/components/key_pad_menu/_key_pad_menu_item.scss @@ -31,11 +31,14 @@ @include euiSlightShadow; border-color: $euiBorderColor; - .euiKeyPadMenuItem__icon { // sass-lint:disable-line nesting-depth - transform: translateY(0); + // sass-lint:disable-block mixins-before-declarations + // sass-lint:disable-block nesting-depth + @include euiAnimationsAllowed { + .euiKeyPadMenuItem__icon { + transform: translateY(0); + } } } - } &:disabled { @@ -93,8 +96,8 @@ .euiKeyPadMenuItem__icon { transition: transform $euiAnimSpeedNormal $euiAnimSlightBounce; - transform: translateY(2px); margin-bottom: $euiSizeM; + transform: translateY(2px); } .euiKeyPadMenuItem__label { diff --git a/src/global_styling/mixins/_button.scss b/src/global_styling/mixins/_button.scss index 70dd6b5c869..9c919c17b06 100644 --- a/src/global_styling/mixins/_button.scss +++ b/src/global_styling/mixins/_button.scss @@ -14,7 +14,7 @@ // Adds the focus (and hover) animation for translating up 1px @mixin euiButtonFocus { - @media screen and (prefers-reduced-motion: no-preference) { + @include euiAnimationsAllowed { transition: transform $euiAnimSpeedNormal ease-in-out, background $euiAnimSpeedNormal ease-in-out; &:hover:not([class*='isDisabled']) { diff --git a/src/global_styling/mixins/_helpers.scss b/src/global_styling/mixins/_helpers.scss index 196e75a9c76..671b314922f 100644 --- a/src/global_styling/mixins/_helpers.scss +++ b/src/global_styling/mixins/_helpers.scss @@ -88,3 +88,11 @@ @content; } } + +// Wrap animations with this mixin to ensure they don't get applied +// for users who have turned off the motion setting +@mixin euiAnimationsAllowed { + @media screen and (prefers-reduced-motion: no-preference) { + @content; + } +} diff --git a/src/themes/eui-amsterdam/overrides/_index.scss b/src/themes/eui-amsterdam/overrides/_index.scss index 105addb80b5..6d4fc78f560 100644 --- a/src/themes/eui-amsterdam/overrides/_index.scss +++ b/src/themes/eui-amsterdam/overrides/_index.scss @@ -11,6 +11,7 @@ @import 'form_controls'; @import 'header'; @import 'image'; +@import 'key_pad_menu'; @import 'mark'; @import 'modal'; @import 'overlay_mask'; diff --git a/src/themes/eui-amsterdam/overrides/_key_pad_menu.scss b/src/themes/eui-amsterdam/overrides/_key_pad_menu.scss new file mode 100644 index 00000000000..09ffcd6f4a4 --- /dev/null +++ b/src/themes/eui-amsterdam/overrides/_key_pad_menu.scss @@ -0,0 +1,34 @@ +/** + * 1. If this class is applied to a button, we need to override the Chrome default font. + * 2. If it has a BetaBadge, make sure only the first letter shows + */ +.euiKeyPadMenuItem { + @include euiInteractiveStates; + border: none; + // Disable indentation for transition legibility + // sass-lint:disable-block indentation + transition: + background-color $euiAnimSpeedFast ease-in, + box-shadow $euiAnimSpeedFast ease-in; + + // Using the aria selectors to ensure proper a11y + &[aria-pressed='true'], + &[aria-current] { + @include euiFocusBackground; + color: $euiColorPrimaryText; + } + + &:not(:disabled) { + &:hover, + &:focus { + @include euiBottomShadowSmall; + } + } +} + +.euiKeyPadMenuItem__label { + font-size: $euiFontSizeXS; + font-weight: $euiFontWeightSemiBold; + line-height: $euiSize; + text-align: center; +} From 8b2e8d5fbf1831fc40ad7d86c3da43c8ce203a26 Mon Sep 17 00:00:00 2001 From: cchaos Date: Fri, 20 Nov 2020 12:41:40 -0500 Subject: [PATCH 03/12] Very manual addition of `checkable` --- .../key_pad_menu/key_pad_menu_checkable.js | 81 ++++++++ .../key_pad_menu/key_pad_menu_example.js | 24 +++ .../key_pad_menu_item.test.tsx.snap | 113 ++++++++-- .../key_pad_menu/_key_pad_menu_item.scss | 47 +++-- .../key_pad_menu/key_pad_menu_item.test.tsx | 22 ++ .../key_pad_menu/key_pad_menu_item.tsx | 195 ++++++++++++++---- .../overrides/_key_pad_menu.scss | 29 ++- 7 files changed, 418 insertions(+), 93 deletions(-) create mode 100644 src-docs/src/views/key_pad_menu/key_pad_menu_checkable.js diff --git a/src-docs/src/views/key_pad_menu/key_pad_menu_checkable.js b/src-docs/src/views/key_pad_menu/key_pad_menu_checkable.js new file mode 100644 index 00000000000..57c3d38cbd7 --- /dev/null +++ b/src-docs/src/views/key_pad_menu/key_pad_menu_checkable.js @@ -0,0 +1,81 @@ +import React, { useState } from 'react'; + +import { + EuiIcon, + EuiKeyPadMenu, + EuiKeyPadMenuItem, +} from '../../../../src/components'; + +export default () => { + const [singleSelectedID, setSingleSelectedID] = useState( + 'singleKeypadSelect3' + ); + const [multiSelect1isSelected, setmultiSelect1isSelected] = useState(false); + const [multiSelect3isSelected, setmultiSelect3isSelected] = useState(false); + + return ( + <> + + { + setSingleSelectedID(id); + }} + isSelected={singleSelectedID === 'singleKeypadSelect1'}> + + + + + + { + setSingleSelectedID(id); + }} + isSelected={singleSelectedID === 'singleKeypadSelect3'}> + + + + + { + setmultiSelect1isSelected((selected) => !selected); + }}> + + + + + + { + setmultiSelect3isSelected((selected) => !selected); + }}> + + + + + ); +}; diff --git a/src-docs/src/views/key_pad_menu/key_pad_menu_example.js b/src-docs/src/views/key_pad_menu/key_pad_menu_example.js index e2ee7f6b150..cdbfb153c00 100644 --- a/src-docs/src/views/key_pad_menu/key_pad_menu_example.js +++ b/src-docs/src/views/key_pad_menu/key_pad_menu_example.js @@ -48,6 +48,10 @@ const keyPadBetaSnippet = ` `; +import KeyPadCheckable from './key_pad_menu_checkable'; +const keyPadCheckableSource = require('!!raw-loader!./key_pad_menu_checkable'); +const keyPadCheckableHtml = renderToHtml(KeyPadCheckable); + export const KeyPadMenuExample = { title: 'Key pad menu', sections: [ @@ -141,6 +145,26 @@ export const KeyPadMenuExample = { snippet: keyPadBetaSnippet, demo: , }, + { + title: 'Checkable', + source: [ + { + type: GuideSectionTypes.JS, + code: keyPadCheckableSource, + }, + { + type: GuideSectionTypes.HTML, + code: keyPadCheckableHtml, + }, + ], + text: ( +
+

...

+
+ ), + // snippet: keyPadBetaSnippet, + demo: , + }, ], playground: keyPadMenuItemConfig, }; diff --git a/src/components/key_pad_menu/__snapshots__/key_pad_menu_item.test.tsx.snap b/src/components/key_pad_menu/__snapshots__/key_pad_menu_item.test.tsx.snap index 03e4d1fd345..18ec2d8ff23 100644 --- a/src/components/key_pad_menu/__snapshots__/key_pad_menu_item.test.tsx.snap +++ b/src/components/key_pad_menu/__snapshots__/key_pad_menu_item.test.tsx.snap @@ -1,5 +1,74 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`EuiKeyPadMenuItem checkable renders as checkbox 1`] = ` +