diff --git a/scripts/release.js b/scripts/release.js index 49fc4f5a014..82cfdcee00c 100644 --- a/scripts/release.js +++ b/scripts/release.js @@ -11,6 +11,7 @@ const { collateChangelogFiles, updateChangelog, } = require('./update-changelog'); +const updateDocsVersionSwitcher = require('./update-versions-log'); const TYPE_MAJOR = 0; const TYPE_MINOR = 1; @@ -73,6 +74,9 @@ if (args.dry_run) { // Update CHANGELOG.md updateChangelog(changelog, versionTarget); + // Update version switcher data + updateDocsVersionSwitcher(versionTarget); + // update package.json & package-lock.json version, git commit, git tag execSync(`npm version ${versionTarget}`, execOptions); } diff --git a/scripts/tests/update-versions-log.test.js b/scripts/tests/update-versions-log.test.js new file mode 100644 index 00000000000..d32978e6091 --- /dev/null +++ b/scripts/tests/update-versions-log.test.js @@ -0,0 +1,64 @@ +import updateDocsVersionSwitcher from '../update-versions-log'; + +// Mock files +jest.mock('fs', () => ({ + readFileSync: jest.fn(() => ''), + writeFileSync: jest.fn(), +})); +const fs = require('fs'); +jest.mock('child_process', () => ({ + execSync: jest.fn(), +})); +const { execSync } = require('child_process'); + +describe('updateDocsVersionSwitcher', () => { + const mockInput = `{ + "euiVersions": [ + "3.0.0", + "2.0.0", + "1.0.0" + ] +}`; + const expectedOutput = `{ + "euiVersions": [ + "4.0.0", + "3.0.0", + "2.0.0", + "1.0.0" + ] +}`; + + beforeEach(() => jest.clearAllMocks()); + + it('appends a new version to the top of the array list', () => { + fs.readFileSync.mockReturnValue(mockInput); + + updateDocsVersionSwitcher('4.0.0'); + + expect(fs.writeFileSync).toHaveBeenCalledWith( + expect.stringContaining('versions.json'), + expectedOutput + ); + expect(execSync).toHaveBeenCalledWith( + expect.stringMatching(/^git add .+versions\.json$/) + ); + }); + + it('throws an error if the version is missing', () => { + expect(() => updateDocsVersionSwitcher('')).toThrow('Missing version'); + }); + + it('throws an error the version is already at the start of the versions array', () => { + fs.readFileSync.mockReturnValue(mockInput); + expect(() => updateDocsVersionSwitcher('3.0.0')).toThrow( + 'Current version has already been logged' + ); + }); + + it('throws an error if the JSON data is somehow malformed', () => { + fs.readFileSync.mockReturnValue('{}'); + expect(() => updateDocsVersionSwitcher('4.0.0')).toThrow( + 'Invalid JSON data' + ); + }); +}); diff --git a/scripts/update-versions-log.js b/scripts/update-versions-log.js new file mode 100644 index 00000000000..13f9a213a6d --- /dev/null +++ b/scripts/update-versions-log.js @@ -0,0 +1,40 @@ +const path = require('path'); +const fs = require('fs'); +const { execSync } = require('child_process'); + +const versionsLogFile = path.resolve( + __dirname, + '../src-docs/src/components/guide_page/versions.json' +); + +/** + * Writes to the above `versions.json` file (which is what the docs version switcher + * uses to generate its list of versions) with the latest release + * + * To test locally, run `node -e "require('./scripts/update-versions-log')('vX.Y.Z')"` + */ +const updateDocsVersionSwitcher = (versionToAdd, file = versionsLogFile) => { + if (!versionToAdd) { + throw new Error('Missing version'); + } + + let fileString = fs.readFileSync(file).toString(); + const split = `"euiVersions": [`; + const array = fileString.split(split); + + if (array.length <= 1) { + throw new Error(`Invalid JSON data - missing ${split}`); + } + if (array[1].trimStart().startsWith(`"${versionToAdd}`)) { + throw new Error('Current version has already been logged'); + } + + // Prepend the new version and re-form the file string + array[1] = `\n "${versionToAdd}",` + array[1]; + const updatedFileString = array.join(split); + + fs.writeFileSync(file, updatedFileString); + execSync(`git add ${file}`); +}; + +module.exports = updateDocsVersionSwitcher; diff --git a/src-docs/src/components/guide_page/guide_page_header.tsx b/src-docs/src/components/guide_page/guide_page_header.tsx index 09ac1a87491..5db702daf22 100644 --- a/src-docs/src/components/guide_page/guide_page_header.tsx +++ b/src-docs/src/components/guide_page/guide_page_header.tsx @@ -1,4 +1,5 @@ -import React, { useState } from 'react'; +import React, { useState, useMemo } from 'react'; +import { FixedSizeList } from 'react-window'; import { EuiBadge, @@ -9,6 +10,7 @@ import { EuiHeaderLogo, EuiHeaderSectionItemButton, EuiIcon, + EuiListGroupItem, EuiPopover, EuiToolTip, } from '../../../../src/components'; @@ -19,7 +21,11 @@ import { CodeSandboxLink } from '../../components/codesandbox/link'; import logoEUI from '../../images/logo-eui.svg'; import { GuideThemeSelector, GuideFigmaLink } from '../guide_theme_selector'; -const pkg = require('../../../../package.json'); +const { euiVersions } = require('./versions.json'); +const currentVersion = require('../../../../package.json').version; +const pronounceVersion = (version: string) => { + return `version ${version.replaceAll('.', ' point ')}`; // NVDA pronounciation issue +}; export type GuidePageHeaderProps = { onToggleLocale: () => {}; @@ -32,29 +38,76 @@ export const GuidePageHeader: React.FunctionComponent = ({ }) => { const isMobileSize = useIsWithinBreakpoints(['xs', 's']); - function renderLogo() { + const logo = useMemo(() => { return ( Elastic UI ); - } + }, []); - function renderVersion() { + const [isVersionPopoverOpen, setIsVersionPopoverOpen] = useState(false); + const versionBadge = useMemo(() => { const isLocalDev = window.location.host.includes('803'); - return ( setIsVersionPopoverOpen((isOpen) => !isOpen)} + onClickAriaLabel={`${ + isLocalDev ? 'Local' : pronounceVersion(currentVersion) + }. Click to switch versions`} color={isLocalDev ? 'accent' : 'default'} > - {isLocalDev ? 'Local' : `v${pkg.version}`} + {isLocalDev ? 'Local' : `v${currentVersion}`} ); - } + }, []); + const versionSwitcher = useMemo(() => { + return ( + setIsVersionPopoverOpen(false)} + button={versionBadge} + repositionOnScroll + panelPaddingSize="xs" + > + + {({ index, style }) => { + const version = euiVersions[index]; + const screenReaderVersion = pronounceVersion(version); + return ( + + ); + }} + + + ); + }, [isVersionPopoverOpen, versionBadge]); - function renderGithub() { + const github = useMemo(() => { const href = 'https://github.com/elastic/eui'; const label = 'EUI GitHub repo'; return isMobileSize ? ( @@ -68,9 +121,9 @@ export const GuidePageHeader: React.FunctionComponent = ({ ); - } + }, [isMobileSize]); - function renderCodeSandbox() { + const codesandbox = useMemo(() => { const label = 'Codesandbox'; return isMobileSize ? ( @@ -87,11 +140,11 @@ export const GuidePageHeader: React.FunctionComponent = ({ ); - } + }, [isMobileSize]); const [mobilePopoverIsOpen, setMobilePopoverIsOpen] = useState(false); - function renderMobileMenu() { + const mobileMenu = useMemo(() => { const button = ( = ({ gutterSize="none" responsive={false} > - {renderGithub()} + {github} - {renderCodeSandbox()} + {codesandbox} ); - } + }, [mobilePopoverIsOpen, codesandbox, github]); const rightSideItems = isMobileSize ? [ @@ -129,16 +182,16 @@ export const GuidePageHeader: React.FunctionComponent = ({ onToggleLocale={onToggleLocale} selectedLocale={selectedLocale} />, - renderMobileMenu(), + mobileMenu, ] : [ , - renderGithub(), + github, , - renderCodeSandbox(), + codesandbox, ]; return ( @@ -147,7 +200,7 @@ export const GuidePageHeader: React.FunctionComponent = ({ position="fixed" theme="dark" sections={[ - { items: [renderLogo(), renderVersion()] }, + { items: [logo, versionSwitcher] }, { items: rightSideItems }, ]} /> diff --git a/src-docs/src/components/guide_page/versions.json b/src-docs/src/components/guide_page/versions.json new file mode 100644 index 00000000000..b9ae343e267 --- /dev/null +++ b/src-docs/src/components/guide_page/versions.json @@ -0,0 +1,192 @@ +{ + "euiVersions": [ + "89.1.0", + "89.0.0", + "88.5.4", + "88.5.3", + "88.5.2", + "88.5.1", + "88.5.0", + "88.4.0", + "88.3.0", + "88.2.0", + "88.1.0", + "88.0.0", + "87.2.0", + "87.1.0", + "87.0.0", + "86.0.0", + "85.1.0", + "85.0.0", + "84.0.0", + "83.1.0", + "83.0.0", + "82.2.0", + "82.1.0", + "82.0.0", + "81.3.0", + "81.2.0", + "81.1.0", + "81.0.0", + "80.0.0", + "79.0.1", + "79.0.0", + "78.0.0", + "77.2.0", + "77.0.0", + "76.4.0", + "76.3.0", + "76.2.0", + "76.1.0", + "76.0.1", + "76.0.0", + "75.1.1", + "75.1.0", + "75.0.0", + "74.1.0", + "74.0.0", + "73.0.0", + "72.2.0", + "72.1.0", + "72.0.0", + "71.1.0", + "71.0.0", + "70.4.0", + "70.3.0", + "70.2.0", + "70.1.0", + "70.0.0", + "69.0.0", + "68.0.0", + "67.1.1", + "67.1.0", + "67.0.0", + "66.0.0", + "65.0.2", + "65.0.1", + "65.0.0", + "64.0.1", + "64.0.0", + "63.0.0", + "62.2.0", + "62.1.0", + "62.0.1", + "62.0.0", + "61.0.0", + "60.3.0", + "60.2.0", + "60.1.2", + "60.1.1", + "60.1.0", + "60.0.0", + "59.1.0", + "59.0.1", + "59.0.0", + "58.1.1", + "58.1.0", + "58.0.0", + "57.0.0", + "56.0.0", + "55.1.0", + "55.0.1", + "55.0.0", + "54.1.0", + "54.0.0", + "53.0.1", + "53.0.0", + "52.2.0", + "52.1.0", + "52.0.0", + "51.1.0", + "51.0.0", + "49.0.0", + "48.1.0", + "47.0.0", + "46.2.0", + "46.1.0", + "46.0.0", + "45.0.0", + "43.0.0", + "42.1.0", + "42.0.0", + "41.4.0", + "41.3.0", + "41.2.0", + "41.2.0", + "41.1.0", + "41.0.0", + "40.1.0", + "40.0.0", + "39.1.0", + "39.0.0", + "38.2.0", + "38.1.0", + "38.0.0", + "37.7.0", + "37.6.2", + "37.6.0", + "37.5.0", + "37.4.0", + "37.3.1", + "37.3.0", + "37.2.0", + "37.1.0", + "37.0.0", + "36.1.0", + "36.0.0", + "35.1.0", + "35.0.0", + "34.6.0", + "34.5.2", + "34.5.1", + "34.5.0", + "34.4.0", + "34.3.0", + "34.2.0", + "34.1.0", + "34.0.0", + "33.0.0", + "32.2.0", + "32.1.0", + "32.0.4", + "32.0.3", + "32.0.2", + "32.0.1", + "32.0.0", + "31.12.0", + "31.11.0", + "31.10.0", + "31.9.0", + "31.8.0", + "31.7.0", + "31.6.0", + "31.5.0", + "31.4.0", + "31.3.0", + "31.2.0", + "31.1.0", + "31.0.0", + "30.6.0", + "30.5.0", + "30.4.0", + "30.3.0", + "30.2.0", + "30.1.0", + "29.4.0", + "27.0.0", + "26.3.0", + "26.2.0", + "26.1.0", + "26.0.0", + "25.0.0", + "24.1.0", + "24.0.0", + "23.2.0", + "23.1.0", + "23.0.0", + "22.6.0", + "21.0.0", + "20.0.0", + "19.0.0" + ] +} diff --git a/src/components/list_group/__snapshots__/list_group_item.test.tsx.snap b/src/components/list_group/__snapshots__/list_group_item.test.tsx.snap index 8c9b127f0a2..9be40de8636 100644 --- a/src/components/list_group/__snapshots__/list_group_item.test.tsx.snap +++ b/src/components/list_group/__snapshots__/list_group_item.test.tsx.snap @@ -339,10 +339,10 @@ exports[`EuiListGroupItem props size xs is rendered 1`] = ` exports[`EuiListGroupItem props style is rendered 1`] = `
  • { - shouldRenderCustomStyles(, { - skip: { style: true }, - }); - // the styles end up on the inner child - shouldRenderCustomStyles(, { - targetSelector: '.euiListGroupItem__text', - skip: { className: true, css: true }, - }); + shouldRenderCustomStyles(); shouldRenderCustomStyles( = ({ rel, className, css: customCss, + style, iconType, icon, iconProps, @@ -343,7 +344,7 @@ export const EuiListGroupItem: FunctionComponent = ({ }; itemContent = ( -
  • +
  • = ({ ); } else { itemContent = ( -
  • +
  • {itemContent} {extraActionNode}
  • diff --git a/src/components/list_group/list_group_item_extra_action.styles.ts b/src/components/list_group/list_group_item_extra_action.styles.ts index ead41d82ee6..e4493b0941b 100644 --- a/src/components/list_group/list_group_item_extra_action.styles.ts +++ b/src/components/list_group/list_group_item_extra_action.styles.ts @@ -21,6 +21,12 @@ export const euiListGroupItemExtraActionStyles = ({ ${euiCanAnimate} { transition: opacity ${euiTheme.animation.fast}; + + /* Unset default EuiButtonIcon animation - extra & used for specificy override */ + &&:hover, + &&:focus { + transform: translateY(0); + } } `, hoverStyles: css` diff --git a/upcoming_changelogs/7298.md b/upcoming_changelogs/7298.md new file mode 100644 index 00000000000..b97adc0aa57 --- /dev/null +++ b/upcoming_changelogs/7298.md @@ -0,0 +1,4 @@ +**Bug fixes** + +- Fixed `EuiListGroupItem` to pass `style` props to the wrapping `
  • ` element alongside `className` and `css`. All other props will be passed to the underlying content. +- Fixed `EuiListGroupItem`'s non-transitioned transform on hover/focus