From 92da565e0816ef1c2670c91a017034c64d99d8e0 Mon Sep 17 00:00:00 2001 From: Bill Coloe Date: Thu, 15 May 2025 16:48:45 -0400 Subject: [PATCH 1/6] Use small size radio buttons for PlanSelection --- .../manager/src/features/components/PlansPanel/PlanSelection.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/manager/src/features/components/PlansPanel/PlanSelection.tsx b/packages/manager/src/features/components/PlansPanel/PlanSelection.tsx index 10777770000..99d8821287e 100644 --- a/packages/manager/src/features/components/PlansPanel/PlanSelection.tsx +++ b/packages/manager/src/features/components/PlansPanel/PlanSelection.tsx @@ -153,6 +153,7 @@ export const PlanSelection = (props: PlanSelectionProps) => { } id={plan.id} onChange={() => onSelect(plan.id)} + size="small" /> } label={plan.heading} From 616d72ffc61780ea13a450d72ab73d2a4a24cbe1 Mon Sep 17 00:00:00 2001 From: Bill Coloe Date: Fri, 16 May 2025 10:35:19 -0400 Subject: [PATCH 2/6] Update Storybook --- .../ui/src/components/Radio/Radio.stories.tsx | 325 +++++++++++++++++- 1 file changed, 306 insertions(+), 19 deletions(-) diff --git a/packages/ui/src/components/Radio/Radio.stories.tsx b/packages/ui/src/components/Radio/Radio.stories.tsx index d87c7293454..36b43e2f65a 100644 --- a/packages/ui/src/components/Radio/Radio.stories.tsx +++ b/packages/ui/src/components/Radio/Radio.stories.tsx @@ -1,6 +1,6 @@ // @todo: modularization - Import from 'ui' package once FormControlLabel is migrated. import { FormControlLabel } from '@mui/material'; -import React from 'react'; +import React, { useState } from 'react'; import { Box } from '../Box'; import { RadioGroup } from '../RadioGroup'; @@ -9,15 +9,82 @@ import { Radio } from './Radio'; import type { RadioProps } from './Radio'; import type { Meta, StoryObj } from '@storybook/react'; -const meta: Meta = { +interface CustomArgs { + checkedState?: 'checked' | 'unchecked'; + state?: 'active' | 'default' | 'disabled' | 'hover' | 'readonly'; +} + +type RadioStoryProps = RadioProps & CustomArgs; + +const meta: Meta = { args: { - defaultChecked: false, - disableFocusRipple: false, - disableRipple: false, - disableTouchRipple: false, - disabled: false, - name: 'radio', - readOnly: false, + checkedState: 'unchecked', + size: 'medium', + state: 'default', + }, + argTypes: { + state: { + options: ['default', 'disabled', 'readonly', 'hover', 'active'], + control: { type: 'radio' }, + description: 'The state to render', + table: { + type: { summary: 'string' }, + defaultValue: { summary: '"default"' }, + }, + }, + checkedState: { + options: ['unchecked', 'checked'], + control: { type: 'radio' }, + description: 'The checked state of the radio button', + table: { + type: { summary: 'string' }, + defaultValue: { summary: '"unchecked"' }, + }, + }, + size: { + control: { + type: 'radio', + }, + options: ['small', 'medium'], + description: 'The size of the component', + table: { + type: { summary: 'string' }, + defaultValue: { summary: '"medium"' }, + }, + }, + disableRipple: { + control: 'boolean', + description: 'If true, the ripple effect is disabled', + table: { + type: { summary: 'boolean' }, + defaultValue: { summary: 'false' }, + }, + }, + disableFocusRipple: { + control: 'boolean', + description: 'If true, the focus ripple effect is disabled', + table: { + type: { summary: 'boolean' }, + defaultValue: { summary: 'false' }, + }, + }, + disableTouchRipple: { + control: 'boolean', + description: 'If true, the touch ripple effect is disabled', + table: { + type: { summary: 'boolean' }, + defaultValue: { summary: 'false' }, + }, + }, + name: { table: { disable: true } }, + slots: { table: { disable: true } }, + slotProps: { table: { disable: true } }, + component: { table: { disable: true } }, + ref: { table: { disable: true } }, + defaultChecked: { table: { disable: true } }, + disabled: { table: { disable: true } }, + readOnly: { table: { disable: true } }, + checked: { table: { disable: true } }, }, component: Radio, decorators: [ @@ -30,24 +97,244 @@ const meta: Meta = { title: 'Foundations/Radio', }; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = { - render: (args: RadioProps) => , + render: (args) => { + const { + state = 'default', + checkedState = 'unchecked', + ...radioProps + } = args; + + let stateProps: Partial = {}; + if (state === 'disabled') { + stateProps = { disabled: true }; + } else if (state === 'readonly') { + stateProps = { readOnly: true }; + } + + let stateStyle = {}; + if (state === 'hover') { + stateStyle = { backgroundColor: 'rgba(0, 0, 0, 0.04)' }; + } else if (state === 'active') { + stateStyle = { backgroundColor: 'rgba(0, 0, 0, 0.08)' }; + } + + const isChecked = checkedState === 'checked'; + + return ( + + + + ); + }, }; -export const Groups: Story = { - name: 'Controlled Radio Groups', - render: () => ( - +export const States: Story = { + name: 'Radio Button States', + render: () => { + return ( + + + Default + + + + + + + + Hover + + + + + + + + + + Active + + + + + + + + + + Disabled + + + + + + + + Read Only + + + + + + + ); + }, +}; + +const RadioGroupsDemo = (props: { size: 'medium' | 'small' }) => { + const { size } = props; + const [selectedValue, setSelectedValue] = useState('Option 1'); + + const handleChange = (event: React.ChangeEvent) => { + setSelectedValue(event.target.value); + }; + + return ( + } + control={} label="Disabled" + value="Disabled" + /> + } + label="Option 1" + value="Option 1" + /> + } + label="Option 2" + value="Option 2" + /> + } + label="Read Only" + value="Read Only" /> - } label="Option 1" value="Option 1" /> - } label="Option 2" value="Option 2" /> - ), + ); +}; + +export const Groups: Story = { + name: 'Controlled Radio Groups', + args: { + size: 'medium', + }, + argTypes: { + size: { + control: { + type: 'radio', + }, + options: ['small', 'medium'], + description: 'The size of the component', + table: { + type: { summary: 'string' }, + defaultValue: { summary: '"medium"' }, + }, + }, + }, + render: (args) => { + return ; + }, +}; + +export const Interactive: Story = { + name: 'Interactive Demo', + parameters: { + controls: { expanded: true }, + }, + args: { + checkedState: 'unchecked', + size: 'medium', + }, + argTypes: { + checkedState: { + options: ['unchecked', 'checked'], + control: { type: 'radio' }, + description: 'The checked state of the radio button', + table: { + type: { summary: 'string' }, + defaultValue: { summary: '"unchecked"' }, + }, + }, + size: { + control: { + type: 'radio', + }, + options: ['small', 'medium'], + description: 'The size of the component', + table: { + type: { summary: 'string' }, + defaultValue: { summary: '"medium"' }, + }, + }, + }, + render: (args) => { + const { checkedState = 'unchecked', ...radioProps } = args; + const isChecked = checkedState === 'checked'; + + return ( + + + + Radio Label + + + ); + }, }; export default meta; From 35db57a1b4143466e2764708b1bf959569abe93b Mon Sep 17 00:00:00 2001 From: Bill Coloe Date: Wed, 21 May 2025 16:14:34 -0500 Subject: [PATCH 3/6] Added changeset: Radio buttons too large on plans table --- packages/manager/.changeset/pr-12261-fixed-1747862074450.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/manager/.changeset/pr-12261-fixed-1747862074450.md diff --git a/packages/manager/.changeset/pr-12261-fixed-1747862074450.md b/packages/manager/.changeset/pr-12261-fixed-1747862074450.md new file mode 100644 index 00000000000..29db545cd6a --- /dev/null +++ b/packages/manager/.changeset/pr-12261-fixed-1747862074450.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +Radio buttons too large on plans table ([#12261](https://github.com/linode/manager/pull/12261)) From 60827b4c878b755dc86e1ac841a5066c99eb384e Mon Sep 17 00:00:00 2001 From: Bill Coloe Date: Wed, 28 May 2025 10:38:35 -0500 Subject: [PATCH 4/6] Add correct small size for dark mode --- packages/ui/src/foundations/themes/dark.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/ui/src/foundations/themes/dark.ts b/packages/ui/src/foundations/themes/dark.ts index 38bce07501a..8b1f3e77ce1 100644 --- a/packages/ui/src/foundations/themes/dark.ts +++ b/packages/ui/src/foundations/themes/dark.ts @@ -791,6 +791,11 @@ export const darkTheme: ThemeOptions = { color: theme.palette.primary.main, }, padding: '10px 10px', + '&.MuiRadio-sizeSmall': { + '.MuiSvgIcon-fontSizeSmall': { + fontSize: '16px', + }, + }, }), }, }, From 3690bbdc1ff34bdf96d8a5f812848303b3017653 Mon Sep 17 00:00:00 2001 From: Bill Coloe Date: Wed, 28 May 2025 11:07:06 -0500 Subject: [PATCH 5/6] Add padding exception next to radio buttons --- .../manager/src/features/components/PlansPanel/PlanSelection.tsx | 1 + .../src/features/components/PlansPanel/PlanSelectionTable.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/manager/src/features/components/PlansPanel/PlanSelection.tsx b/packages/manager/src/features/components/PlansPanel/PlanSelection.tsx index 99d8821287e..563c66b59d6 100644 --- a/packages/manager/src/features/components/PlansPanel/PlanSelection.tsx +++ b/packages/manager/src/features/components/PlansPanel/PlanSelection.tsx @@ -163,6 +163,7 @@ export const PlanSelection = (props: PlanSelectionProps) => { {plan.heading}   {showDisabledTooltip && ( diff --git a/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx b/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx index d687f8720bc..62c8780490e 100644 --- a/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx +++ b/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx @@ -130,6 +130,7 @@ export const PlanSelectionTable = (props: PlanSelectionTableProps) => { isPlanCell={isPlanCell} key={testId} noWrap={noWrap} + sx={isPlanCell ? { paddingLeft: 0.5 } : undefined} > {isPlanCell && filterOptions?.header ? filterOptions?.header From 18673864e27da8f5c26be5b2447e40e2968fb2e7 Mon Sep 17 00:00:00 2001 From: Bill Coloe Date: Thu, 29 May 2025 11:30:10 -0500 Subject: [PATCH 6/6] Add individual stories for states --- .../ui/src/components/Radio/Radio.stories.tsx | 225 ++++++------------ 1 file changed, 79 insertions(+), 146 deletions(-) diff --git a/packages/ui/src/components/Radio/Radio.stories.tsx b/packages/ui/src/components/Radio/Radio.stories.tsx index 36b43e2f65a..40ac58c8d0e 100644 --- a/packages/ui/src/components/Radio/Radio.stories.tsx +++ b/packages/ui/src/components/Radio/Radio.stories.tsx @@ -88,7 +88,7 @@ const meta: Meta = { }, component: Radio, decorators: [ - (Story) => ( + (Story: React.ComponentType) => ( @@ -131,107 +131,63 @@ export const Default: Story = { }, }; -export const States: Story = { - name: 'Radio Button States', - render: () => { - return ( - - - Default - - - - - +export const Unchecked: Story = { + parameters: { + controls: { disable: true }, + }, + render: () => , +}; - - Hover - - - - - - - +export const UncheckedDisabled: Story = { + name: 'Unchecked Disabled', + parameters: { + controls: { disable: true }, + }, + render: () => , +}; - - Active - - - - - - - +export const UncheckedReadOnly: Story = { + name: 'Unchecked Read Only', + parameters: { + controls: { disable: true }, + }, + render: () => , +}; - - Disabled - - - - - +export const Checked: Story = { + parameters: { + controls: { disable: true }, + }, + render: () => , +}; - - Read Only - - - - - - - ); +export const CheckedDisabled: Story = { + name: 'Checked Disabled', + parameters: { + controls: { disable: true }, + }, + render: () => , +}; + +export const CheckedReadOnly: Story = { + name: 'Checked Read Only', + parameters: { + controls: { disable: true }, }, + render: () => , +}; + +export const SmallSize: Story = { + name: 'Small Size', + parameters: { + controls: { disable: true }, + }, + render: () => ( + + + + + ), }; const RadioGroupsDemo = (props: { size: 'medium' | 'small' }) => { @@ -259,16 +215,15 @@ const RadioGroupsDemo = (props: { size: 'medium' | 'small' }) => { label="Option 2" value="Option 2" /> - } - label="Read Only" - value="Read Only" - /> ); }; -export const Groups: Story = { +interface GroupsArgs { + size: 'medium' | 'small'; +} + +export const Groups: StoryObj = { name: 'Controlled Radio Groups', args: { size: 'medium', @@ -287,54 +242,32 @@ export const Groups: Story = { }, }, render: (args) => { - return ; + return ; }, }; -export const Interactive: Story = { - name: 'Interactive Demo', +export const WithLabel: Story = { + name: 'With Label', parameters: { - controls: { expanded: true }, - }, - args: { - checkedState: 'unchecked', - size: 'medium', - }, - argTypes: { - checkedState: { - options: ['unchecked', 'checked'], - control: { type: 'radio' }, - description: 'The checked state of the radio button', - table: { - type: { summary: 'string' }, - defaultValue: { summary: '"unchecked"' }, - }, - }, - size: { - control: { - type: 'radio', - }, - options: ['small', 'medium'], - description: 'The size of the component', - table: { - type: { summary: 'string' }, - defaultValue: { summary: '"medium"' }, - }, - }, - }, - render: (args) => { - const { checkedState = 'unchecked', ...radioProps } = args; - const isChecked = checkedState === 'checked'; - - return ( - - - - Radio Label - - - ); + controls: { disable: true }, }, + render: () => ( + + } label="Unchecked with label" /> + } + label="Checked with label" + /> + } + label="Disabled with label" + /> + } + label="Checked disabled with label" + /> + + ), }; export default meta;