diff --git a/packages/ui-components/Common/ChangeHistory/index.module.css b/packages/ui-components/Common/ChangeHistory/index.module.css new file mode 100644 index 0000000000000..c69d0e91896fa --- /dev/null +++ b/packages/ui-components/Common/ChangeHistory/index.module.css @@ -0,0 +1,83 @@ +@reference "../../styles/index.css"; + +.summary { + @apply outline-hidden + flex + h-9 + cursor-pointer + select-none + items-center + gap-2 + rounded-md + border + border-neutral-200 + p-2 + text-sm + text-neutral-700 + motion-safe:transition-colors + dark:border-neutral-900 + dark:text-neutral-300; + + &:hover, + &:focus-visible { + @apply bg-neutral-100 + dark:bg-neutral-900; + } +} + +.dropdownContentWrapper { + @apply absolute + right-0 + top-full + z-50 + mt-1 + max-h-80 + w-52 + overflow-hidden + rounded-sm + border + border-neutral-200 + bg-white + shadow-lg + dark:border-neutral-900 + dark:bg-neutral-950; +} + +.dropdownContentInner { + @apply max-h-80 + w-52 + overflow-y-auto; +} + +.dropdownItem { + @apply outline-hidden + block + px-2.5 + py-1.5 + text-sm + font-medium + text-neutral-800 + no-underline + motion-safe:transition-colors + dark:text-white; + + &:hover, + &:focus-visible { + @apply bg-green-600 + text-white; + } +} + +.dropdownLabel { + @apply block + text-sm + font-medium + leading-tight; +} + +.dropdownVersions { + @apply block + text-xs + leading-tight + opacity-75; +} diff --git a/packages/ui-components/Common/ChangeHistory/index.stories.tsx b/packages/ui-components/Common/ChangeHistory/index.stories.tsx new file mode 100644 index 0000000000000..f40b27a89a8ed --- /dev/null +++ b/packages/ui-components/Common/ChangeHistory/index.stories.tsx @@ -0,0 +1,130 @@ +import type { Meta as MetaObj, StoryObj } from '@storybook/react'; + +import ChangeHistory from '#ui/Common/ChangeHistory'; + +type Story = StoryObj; +type Meta = MetaObj; + +const SAMPLE_CHANGES = [ + { + versions: ['v15.4.0'], + label: 'No longer experimental', + url: 'https://github.com/nodejs/node/pull/12345', + }, + { + versions: ['v15.0.0', 'v14.17.0'], + label: 'Added in v15.0.0, v14.17.0', + url: 'https://github.com/nodejs/node/pull/67890', + }, + { + versions: ['v16.0.0'], + label: 'Deprecated in 16', + }, +]; + +const LARGE_SAMPLE_CHANGES = [ + { + versions: ['v20.0.0'], + label: 'Breaking change in v20', + url: 'https://github.com/nodejs/node/pull/50001', + }, + { + versions: ['v19.8.0'], + label: 'Performance improvement', + url: 'https://github.com/nodejs/node/pull/49999', + }, + { + versions: ['v19.0.0'], + label: 'API redesign', + url: 'https://github.com/nodejs/node/pull/49000', + }, + { + versions: ['v18.17.0', 'v18.16.1'], + label: 'Security fix backported', + url: 'https://github.com/nodejs/node/pull/48500', + }, + { + versions: ['v18.0.0'], + label: 'Major version release', + url: 'https://github.com/nodejs/node/pull/47000', + }, + { + versions: ['v17.9.0'], + label: 'Experimental feature added', + url: 'https://github.com/nodejs/node/pull/46500', + }, + { + versions: ['v17.0.0'], + label: 'Node.js 17 release', + url: 'https://github.com/nodejs/node/pull/45000', + }, + { + versions: ['v16.15.0', 'v16.14.2'], + label: 'Bug fix release', + url: 'https://github.com/nodejs/node/pull/44000', + }, + { + versions: ['v16.0.0'], + label: 'Deprecated in v16', + url: 'https://github.com/nodejs/node/pull/43000', + }, + { + versions: ['v15.14.0'], + label: 'Feature enhancement', + url: 'https://github.com/nodejs/node/pull/42000', + }, + { + versions: ['v15.0.0', 'v14.17.0'], + label: 'Initial implementation', + url: 'https://github.com/nodejs/node/pull/41000', + }, + { + versions: ['v14.18.0'], + label: 'Documentation update', + url: 'https://github.com/nodejs/node/pull/40000', + }, + { + versions: ['v14.0.0'], + label: 'Added to stable API', + url: 'https://github.com/nodejs/node/pull/39000', + }, + { + versions: ['v13.14.0'], + label: 'Experimental flag removed', + url: 'https://github.com/nodejs/node/pull/38000', + }, + { + versions: ['v12.22.0', 'v12.21.0'], + label: 'Backported to LTS', + url: 'https://github.com/nodejs/node/pull/37000', + }, + { + versions: ['v12.0.0'], + label: 'First experimental version', + url: 'https://github.com/nodejs/node/pull/36000', + }, +]; + +export const Default: Story = { + render: args => ( +
+ +
+ ), + args: { + changes: SAMPLE_CHANGES, + }, +}; + +export const LargeHistory: Story = { + render: args => ( +
+ +
+ ), + args: { + changes: LARGE_SAMPLE_CHANGES, + }, +}; + +export default { component: ChangeHistory } as Meta; diff --git a/packages/ui-components/Common/ChangeHistory/index.tsx b/packages/ui-components/Common/ChangeHistory/index.tsx new file mode 100644 index 0000000000000..3beaacc845186 --- /dev/null +++ b/packages/ui-components/Common/ChangeHistory/index.tsx @@ -0,0 +1,67 @@ +import { ChevronDownIcon, ClockIcon } from '@heroicons/react/24/outline'; +import classNames from 'classnames'; +import type { FC, ComponentProps } from 'react'; + +import type { LinkLike } from '#ui/types.js'; + +import styles from './index.module.css'; + +export type HistoryChange = { + versions: Array; + label: string; + url?: string; +}; + +type ChangeHistoryProps = ComponentProps<'div'> & { + label: string; + changes: Array; + as?: LinkLike; +}; + +const ChangeHistory: FC = ({ + label = 'History', + changes = [], + className, + as: As = 'a', + 'aria-label': ariaLabel = label, + ...props +}) => ( +
+
+ + + {label} + + +
+
+ {changes.map((change, index) => { + const MenuItem = change.url ? As : 'div'; + + return ( + +
{change.label}
+
+ {change.versions.join(', ')} +
+
+ ); + })} +
+
+
+
+); + +export default ChangeHistory;