diff --git a/app/components/AskOrganizer/AskOrganizer.container.tsx b/app/components/AskOrganizer/AskOrganizer.container.tsx index 1e6376d..6614bac 100644 --- a/app/components/AskOrganizer/AskOrganizer.container.tsx +++ b/app/components/AskOrganizer/AskOrganizer.container.tsx @@ -78,9 +78,7 @@ const AskOrganizerContainer = memo( buttonHref, }, onContactClick as - | (( - _data: Record, - ) => void) + | ((_data: Record) => void) | undefined, ); @@ -111,4 +109,3 @@ const AskOrganizerContainer = memo( AskOrganizerContainer.displayName = "AskOrganizer"; export default AskOrganizerContainer; - diff --git a/app/components/AskOrganizer/AskOrganizer.types.ts b/app/components/AskOrganizer/AskOrganizer.types.ts index c6514bc..e7bd031 100644 --- a/app/components/AskOrganizer/AskOrganizer.types.ts +++ b/app/components/AskOrganizer/AskOrganizer.types.ts @@ -37,7 +37,6 @@ export interface AskOrganizerViewProps { variant: AskOrganizerVariant; labelledBy?: string; onContactClick: ( - event: React.MouseEvent, + _event: React.MouseEvent, ) => void; } - diff --git a/app/components/AskOrganizer/AskOrganizer.view.tsx b/app/components/AskOrganizer/AskOrganizer.view.tsx index 18d5cca..c16d816 100644 --- a/app/components/AskOrganizer/AskOrganizer.view.tsx +++ b/app/components/AskOrganizer/AskOrganizer.view.tsx @@ -53,4 +53,3 @@ function AskOrganizerView({ } export default AskOrganizerView; - diff --git a/app/components/AskOrganizer/index.tsx b/app/components/AskOrganizer/index.tsx index ed9b85a..49e4669 100644 --- a/app/components/AskOrganizer/index.tsx +++ b/app/components/AskOrganizer/index.tsx @@ -1,3 +1,2 @@ export { default } from "./AskOrganizer.container"; export * from "./AskOrganizer.types"; - diff --git a/app/components/Checkbox/Checkbox.types.ts b/app/components/Checkbox/Checkbox.types.ts index 4f71113..2a0d28d 100644 --- a/app/components/Checkbox/Checkbox.types.ts +++ b/app/components/Checkbox/Checkbox.types.ts @@ -34,6 +34,6 @@ export interface CheckboxViewProps { checkGlyphColor: string; labelColor: string; accessibilityProps: React.HTMLAttributes; - onToggle: (e: React.MouseEvent | React.KeyboardEvent) => void; - onKeyDown: (e: React.KeyboardEvent) => void; + onToggle: (_e: React.MouseEvent | React.KeyboardEvent) => void; + onKeyDown: (_e: React.KeyboardEvent) => void; } diff --git a/app/components/ContentContainer/ContentContainer.view.tsx b/app/components/ContentContainer/ContentContainer.view.tsx index c8b6a7c..9233107 100644 --- a/app/components/ContentContainer/ContentContainer.view.tsx +++ b/app/components/ContentContainer/ContentContainer.view.tsx @@ -38,9 +38,7 @@ function ContentContainerView({

{post.frontmatter.title}

{/* Description */} -

- {post.frontmatter.description} -

+

{post.frontmatter.description}

diff --git a/app/components/ContentLockup/ContentLockup.view.tsx b/app/components/ContentLockup/ContentLockup.view.tsx index b8cac7b..6f13269 100644 --- a/app/components/ContentLockup/ContentLockup.view.tsx +++ b/app/components/ContentLockup/ContentLockup.view.tsx @@ -63,9 +63,7 @@ function ContentLockupView({ {/* Subtitle */} - {subtitle ? ( -

{subtitle}

- ) : null} + {subtitle ?

{subtitle}

: null} {/* Description */} @@ -94,11 +92,7 @@ function ContentLockupView({ {/* Large button for md and lg breakpoints */}
-
diff --git a/app/components/ContextMenuItem/ContextMenuItem.types.ts b/app/components/ContextMenuItem/ContextMenuItem.types.ts index fb213b5..1580f10 100644 --- a/app/components/ContextMenuItem/ContextMenuItem.types.ts +++ b/app/components/ContextMenuItem/ContextMenuItem.types.ts @@ -1,5 +1,4 @@ -export interface ContextMenuItemProps - extends React.HTMLAttributes { +export interface ContextMenuItemProps extends React.HTMLAttributes { children?: React.ReactNode; selected?: boolean; hasSubmenu?: boolean; @@ -18,6 +17,6 @@ export interface ContextMenuItemViewProps { disabled: boolean; className: string; itemClasses: string; - handleClick: (e: React.MouseEvent) => void; - handleKeyDown: (e: React.KeyboardEvent) => void; + handleClick: (_e: React.MouseEvent) => void; + handleKeyDown: (_e: React.KeyboardEvent) => void; } diff --git a/app/components/FeatureGrid/FeatureGrid.container.tsx b/app/components/FeatureGrid/FeatureGrid.container.tsx index b440557..1ef87a8 100644 --- a/app/components/FeatureGrid/FeatureGrid.container.tsx +++ b/app/components/FeatureGrid/FeatureGrid.container.tsx @@ -61,4 +61,3 @@ const FeatureGridContainer = memo( FeatureGridContainer.displayName = "FeatureGrid"; export default FeatureGridContainer; - diff --git a/app/components/FeatureGrid/FeatureGrid.types.ts b/app/components/FeatureGrid/FeatureGrid.types.ts index 09e1151..31e061a 100644 --- a/app/components/FeatureGrid/FeatureGrid.types.ts +++ b/app/components/FeatureGrid/FeatureGrid.types.ts @@ -17,4 +17,3 @@ export interface FeatureGridViewProps extends FeatureGridProps { features: Feature[]; labelledBy?: string; } - diff --git a/app/components/FeatureGrid/FeatureGrid.view.tsx b/app/components/FeatureGrid/FeatureGrid.view.tsx index c5bbe92..4429d93 100644 --- a/app/components/FeatureGrid/FeatureGrid.view.tsx +++ b/app/components/FeatureGrid/FeatureGrid.view.tsx @@ -50,4 +50,3 @@ function FeatureGridView({ } export default FeatureGridView; - diff --git a/app/components/FeatureGrid/index.tsx b/app/components/FeatureGrid/index.tsx index f077179..8fc1ebd 100644 --- a/app/components/FeatureGrid/index.tsx +++ b/app/components/FeatureGrid/index.tsx @@ -1,3 +1,2 @@ export { default } from "./FeatureGrid.container"; export * from "./FeatureGrid.types"; - diff --git a/app/components/Header/Header.types.ts b/app/components/Header/Header.types.ts index ad3d2bc..b47f5d4 100644 --- a/app/components/Header/Header.types.ts +++ b/app/components/Header/Header.types.ts @@ -31,15 +31,15 @@ export interface HeaderViewProps { | "footerLg"; showText: boolean; }>; - renderNavigationItems: (size: NavSize) => React.ReactNode; - renderLoginButton: (size: NavSize) => React.ReactNode; + renderNavigationItems: (_size: NavSize) => React.ReactNode; + renderLoginButton: (_size: NavSize) => React.ReactNode; renderCreateRuleButton: ( - buttonSize: "xsmall" | "small" | "medium" | "large" | "xlarge", - containerSize: "small" | "medium" | "large" | "xlarge", - avatarSize: "small" | "medium" | "large" | "xlarge", + _buttonSize: "xsmall" | "small" | "medium" | "large" | "xlarge", + _containerSize: "small" | "medium" | "large" | "xlarge", + _avatarSize: "small" | "medium" | "large" | "xlarge", ) => React.ReactNode; renderLogo: ( - size: + _size: | "default" | "homeHeaderXsmall" | "homeHeaderSm" @@ -52,7 +52,7 @@ export interface HeaderViewProps { | "headerXl" | "footer" | "footerLg", - showText: boolean, + _showText: boolean, ) => React.ReactNode; } diff --git a/app/components/HomeHeader/HomeHeader.types.ts b/app/components/HomeHeader/HomeHeader.types.ts index a82e8e5..2a52c90 100644 --- a/app/components/HomeHeader/HomeHeader.types.ts +++ b/app/components/HomeHeader/HomeHeader.types.ts @@ -35,15 +35,15 @@ export interface HomeHeaderViewProps { | "footerLg"; showText: boolean; }>; - renderNavigationItems: (size: NavSize) => React.ReactNode; - renderLoginButton: (size: NavSize) => React.ReactNode; + renderNavigationItems: (_size: NavSize) => React.ReactNode; + renderLoginButton: (_size: NavSize) => React.ReactNode; renderCreateRuleButton: ( - buttonSize: "xsmall" | "small" | "medium" | "large" | "xlarge", - containerSize: "small" | "medium" | "large" | "xlarge", - avatarSize: "small" | "medium" | "large" | "xlarge", + _buttonSize: "xsmall" | "small" | "medium" | "large" | "xlarge", + _containerSize: "small" | "medium" | "large" | "xlarge", + _avatarSize: "small" | "medium" | "large" | "xlarge", ) => React.ReactNode; renderLogo: ( - size: + _size: | "default" | "homeHeaderXsmall" | "homeHeaderSm" @@ -56,6 +56,6 @@ export interface HomeHeaderViewProps { | "headerXl" | "footer" | "footerLg", - showText: boolean, + _showText: boolean, ) => React.ReactNode; } diff --git a/app/components/Input/Input.types.ts b/app/components/Input/Input.types.ts index 8c150dd..59d4aeb 100644 --- a/app/components/Input/Input.types.ts +++ b/app/components/Input/Input.types.ts @@ -1,5 +1,7 @@ -export interface InputProps - extends Omit, "size" | "onChange" | "onFocus" | "onBlur"> { +export interface InputProps extends Omit< + React.InputHTMLAttributes, + "size" | "onChange" | "onFocus" | "onBlur" +> { size?: "small" | "medium" | "large"; labelVariant?: "default" | "horizontal"; state?: "default" | "active" | "hover" | "focus"; @@ -32,7 +34,7 @@ export interface InputViewProps { labelClasses: string; inputClasses: string; borderRadius: string; - handleChange: (e: React.ChangeEvent) => void; - handleFocus: (e: React.FocusEvent) => void; - handleBlur: (e: React.FocusEvent) => void; + handleChange: (_e: React.ChangeEvent) => void; + handleFocus: (_e: React.FocusEvent) => void; + handleBlur: (_e: React.FocusEvent) => void; } diff --git a/app/components/MenuBarItem/MenuBarItem.types.ts b/app/components/MenuBarItem/MenuBarItem.types.ts index 9aeacfa..d8da10b 100644 --- a/app/components/MenuBarItem/MenuBarItem.types.ts +++ b/app/components/MenuBarItem/MenuBarItem.types.ts @@ -1,5 +1,4 @@ -export interface MenuBarItemProps - extends React.AnchorHTMLAttributes { +export interface MenuBarItemProps extends React.AnchorHTMLAttributes { href?: string; children?: React.ReactNode; variant?: "default" | "home"; diff --git a/app/components/NavigationItem/NavigationItem.types.ts b/app/components/NavigationItem/NavigationItem.types.ts index 1c01611..c5f9de1 100644 --- a/app/components/NavigationItem/NavigationItem.types.ts +++ b/app/components/NavigationItem/NavigationItem.types.ts @@ -1,5 +1,7 @@ -export interface NavigationItemProps - extends Omit, "isActive"> { +export interface NavigationItemProps extends Omit< + React.AnchorHTMLAttributes, + "isActive" +> { href?: string; children?: React.ReactNode; variant?: "default"; diff --git a/app/components/NumberedCards/NumberedCards.container.tsx b/app/components/NumberedCards/NumberedCards.container.tsx index 48c69f1..604fc17 100644 --- a/app/components/NumberedCards/NumberedCards.container.tsx +++ b/app/components/NumberedCards/NumberedCards.container.tsx @@ -33,4 +33,3 @@ const NumberedCardsContainer = memo( NumberedCardsContainer.displayName = "NumberedCards"; export default NumberedCardsContainer; - diff --git a/app/components/NumberedCards/NumberedCards.types.ts b/app/components/NumberedCards/NumberedCards.types.ts index f6ffc86..6250610 100644 --- a/app/components/NumberedCards/NumberedCards.types.ts +++ b/app/components/NumberedCards/NumberedCards.types.ts @@ -13,4 +13,3 @@ export interface NumberedCardsProps { export interface NumberedCardsViewProps extends NumberedCardsProps { schemaJson: string; } - diff --git a/app/components/NumberedCards/NumberedCards.view.tsx b/app/components/NumberedCards/NumberedCards.view.tsx index a606d94..220d109 100644 --- a/app/components/NumberedCards/NumberedCards.view.tsx +++ b/app/components/NumberedCards/NumberedCards.view.tsx @@ -63,4 +63,3 @@ function NumberedCardsView({ } export default NumberedCardsView; - diff --git a/app/components/NumberedCards/index.tsx b/app/components/NumberedCards/index.tsx index f595d6c..1f02634 100644 --- a/app/components/NumberedCards/index.tsx +++ b/app/components/NumberedCards/index.tsx @@ -1,3 +1,2 @@ export { default } from "./NumberedCards.container"; export * from "./NumberedCards.types"; - diff --git a/app/components/QuoteBlock/QuoteBlock.types.ts b/app/components/QuoteBlock/QuoteBlock.types.ts index 192f22b..478aa29 100644 --- a/app/components/QuoteBlock/QuoteBlock.types.ts +++ b/app/components/QuoteBlock/QuoteBlock.types.ts @@ -41,5 +41,5 @@ export interface QuoteBlockViewProps { imageLoading: boolean; currentAvatarSrc: string; onImageLoad: () => void; - onImageError: (error: unknown) => void; + onImageError: (_error: unknown) => void; } diff --git a/app/components/RadioButton/RadioButton.types.ts b/app/components/RadioButton/RadioButton.types.ts index c48dd58..0cfd80d 100644 --- a/app/components/RadioButton/RadioButton.types.ts +++ b/app/components/RadioButton/RadioButton.types.ts @@ -30,6 +30,6 @@ export interface RadioButtonViewProps { backgroundWhenChecked: string; dotColor: string; labelColor: string; - onToggle: (e: React.MouseEvent | React.KeyboardEvent) => void; - onKeyDown: (e: React.KeyboardEvent) => void; + onToggle: (_e: React.MouseEvent | React.KeyboardEvent) => void; + onKeyDown: (_e: React.KeyboardEvent) => void; } diff --git a/app/components/RadioGroup/RadioGroup.types.ts b/app/components/RadioGroup/RadioGroup.types.ts index 620516d..c3d5aef 100644 --- a/app/components/RadioGroup/RadioGroup.types.ts +++ b/app/components/RadioGroup/RadioGroup.types.ts @@ -25,5 +25,5 @@ export interface RadioGroupViewProps { options: RadioOption[]; className: string; ariaLabel?: string; - onOptionChange: (optionValue: string) => void; + onOptionChange: (_optionValue: string) => void; } diff --git a/app/components/RelatedArticles/RelatedArticles.types.ts b/app/components/RelatedArticles/RelatedArticles.types.ts index f61c41e..b7d3878 100644 --- a/app/components/RelatedArticles/RelatedArticles.types.ts +++ b/app/components/RelatedArticles/RelatedArticles.types.ts @@ -11,6 +11,6 @@ export interface RelatedArticlesViewProps { slugOrder: string[]; isMobile: boolean; transformStyle: React.CSSProperties; - getProgressStyle: (index: number) => React.CSSProperties; - onMouseDown?: (e: React.MouseEvent) => void; + getProgressStyle: (_index: number) => React.CSSProperties; + onMouseDown?: (_e: React.MouseEvent) => void; } diff --git a/app/components/RuleCard/RuleCard.types.ts b/app/components/RuleCard/RuleCard.types.ts index 8945847..9776e18 100644 --- a/app/components/RuleCard/RuleCard.types.ts +++ b/app/components/RuleCard/RuleCard.types.ts @@ -14,5 +14,5 @@ export interface RuleCardViewProps { backgroundColor: string; className: string; onClick: () => void; - onKeyDown: (event: React.KeyboardEvent) => void; + onKeyDown: (_event: React.KeyboardEvent) => void; } diff --git a/app/components/RuleStack/RuleStack.container.tsx b/app/components/RuleStack/RuleStack.container.tsx index b69e64b..f3e535f 100644 --- a/app/components/RuleStack/RuleStack.container.tsx +++ b/app/components/RuleStack/RuleStack.container.tsx @@ -36,7 +36,12 @@ const RuleStackContainer = memo(({ className = "" }) => { logger.debug(`${templateName} template clicked`); }; - return ; + return ( + + ); }); RuleStackContainer.displayName = "RuleStack"; diff --git a/app/components/RuleStack/RuleStack.types.ts b/app/components/RuleStack/RuleStack.types.ts index e797cf3..fd635c1 100644 --- a/app/components/RuleStack/RuleStack.types.ts +++ b/app/components/RuleStack/RuleStack.types.ts @@ -4,5 +4,5 @@ export interface RuleStackProps { export interface RuleStackViewProps { className: string; - onTemplateClick: (templateName: string) => void; + onTemplateClick: (_templateName: string) => void; } diff --git a/app/components/Select/Select.container.tsx b/app/components/Select/Select.container.tsx index 628be21..1fab32c 100644 --- a/app/components/Select/Select.container.tsx +++ b/app/components/Select/Select.container.tsx @@ -47,9 +47,10 @@ const SelectContainer = forwardRef( // Sync internal state with external value prop useEffect(() => { - if (value !== undefined) { + if (value !== undefined && value !== selectedValue) { setSelectedValue(value); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [value]); useImperativeHandle( @@ -276,7 +277,6 @@ const SelectContainer = forwardRef( labelVariant={labelVariant} className={className} options={options} - children={children} selectId={selectId} labelId={labelId} isOpen={isOpen} diff --git a/app/components/Select/Select.view.tsx b/app/components/Select/Select.view.tsx index dd9f7ce..1c8ad9b 100644 --- a/app/components/Select/Select.view.tsx +++ b/app/components/Select/Select.view.tsx @@ -26,8 +26,8 @@ export interface SelectViewProps { chevronClasses: string; // Callbacks onButtonClick: () => void; - onButtonKeyDown: (e: React.KeyboardEvent) => void; - onOptionClick: (value: string, text: string) => void; + onButtonKeyDown: (_e: React.KeyboardEvent) => void; + onOptionClick: (_value: string, _text: string) => void; // Refs selectRef: React.RefObject; menuRef: React.RefObject; @@ -38,11 +38,11 @@ export interface SelectViewProps { export function SelectView({ label, - placeholder, + placeholder: _placeholder, size, disabled, - error, - labelVariant, + error: _error, + labelVariant: _labelVariant, options, children, selectId, @@ -118,9 +118,7 @@ export function SelectView({ key={option.value} selected={option.value === selectedValue} size={size} - onClick={() => - onOptionClick(option.value, option.label) - } + onClick={() => onOptionClick(option.value, option.label)} > {option.label} diff --git a/app/components/SelectOption/SelectOption.types.ts b/app/components/SelectOption/SelectOption.types.ts index 39eb1fd..4fca4b9 100644 --- a/app/components/SelectOption/SelectOption.types.ts +++ b/app/components/SelectOption/SelectOption.types.ts @@ -15,6 +15,6 @@ export interface SelectOptionViewProps { disabled: boolean; className: string; itemClasses: string; - handleClick: (e: React.MouseEvent) => void; - handleKeyDown: (e: React.KeyboardEvent) => void; + handleClick: (_e: React.MouseEvent) => void; + handleKeyDown: (_e: React.KeyboardEvent) => void; } diff --git a/app/components/SelectOption/SelectOption.view.tsx b/app/components/SelectOption/SelectOption.view.tsx index 1ea890c..04c1a4a 100644 --- a/app/components/SelectOption/SelectOption.view.tsx +++ b/app/components/SelectOption/SelectOption.view.tsx @@ -1,7 +1,10 @@ import { forwardRef } from "react"; import type { SelectOptionViewProps } from "./SelectOption.types"; -export const SelectOptionView = forwardRef( +export const SelectOptionView = forwardRef< + HTMLDivElement, + SelectOptionViewProps +>( ( { children, diff --git a/app/components/Switch/Switch.types.ts b/app/components/Switch/Switch.types.ts index 63225f8..abaa740 100644 --- a/app/components/Switch/Switch.types.ts +++ b/app/components/Switch/Switch.types.ts @@ -1,5 +1,7 @@ -export interface SwitchProps - extends Omit, "onChange"> { +export interface SwitchProps extends Omit< + React.ButtonHTMLAttributes, + "onChange" +> { checked?: boolean; onChange?: ( _e: @@ -23,8 +25,8 @@ export interface SwitchViewProps { trackClasses: string; thumbClasses: string; labelClasses: string; - onClick: (e: React.MouseEvent) => void; - onKeyDown: (e: React.KeyboardEvent) => void; - onFocus: (e: React.FocusEvent) => void; - onBlur: (e: React.FocusEvent) => void; + onClick: (_e: React.MouseEvent) => void; + onKeyDown: (_e: React.KeyboardEvent) => void; + onFocus: (_e: React.FocusEvent) => void; + onBlur: (_e: React.FocusEvent) => void; } diff --git a/app/components/TextArea/TextArea.types.ts b/app/components/TextArea/TextArea.types.ts index 727070e..0482fea 100644 --- a/app/components/TextArea/TextArea.types.ts +++ b/app/components/TextArea/TextArea.types.ts @@ -1,5 +1,7 @@ -export interface TextAreaProps - extends Omit, "size" | "onChange" | "onFocus" | "onBlur"> { +export interface TextAreaProps extends Omit< + React.TextareaHTMLAttributes, + "size" | "onChange" | "onFocus" | "onBlur" +> { size?: "small" | "medium" | "large"; labelVariant?: "default" | "horizontal"; state?: "default" | "active" | "hover" | "focus"; @@ -33,7 +35,7 @@ export interface TextAreaViewProps { labelClasses: string; textareaClasses: string; borderRadius: string; - handleChange: (e: React.ChangeEvent) => void; - handleFocus: (e: React.FocusEvent) => void; - handleBlur: (e: React.FocusEvent) => void; + handleChange: (_e: React.ChangeEvent) => void; + handleFocus: (_e: React.FocusEvent) => void; + handleBlur: (_e: React.FocusEvent) => void; } diff --git a/app/components/TextArea/TextArea.view.tsx b/app/components/TextArea/TextArea.view.tsx index 04a644c..523a06e 100644 --- a/app/components/TextArea/TextArea.view.tsx +++ b/app/components/TextArea/TextArea.view.tsx @@ -11,7 +11,7 @@ export const TextAreaView = forwardRef( value, name, disabled, - className, + className: _className, rows, containerClasses, labelClasses, diff --git a/app/components/Toggle/Toggle.types.ts b/app/components/Toggle/Toggle.types.ts index b15c267..efd36ff 100644 --- a/app/components/Toggle/Toggle.types.ts +++ b/app/components/Toggle/Toggle.types.ts @@ -1,5 +1,7 @@ -export interface ToggleProps - extends Omit, "onChange"> { +export interface ToggleProps extends Omit< + React.ButtonHTMLAttributes, + "onChange" +> { label?: string; checked?: boolean; onChange?: ( @@ -34,11 +36,11 @@ export interface ToggleViewProps { labelClasses: string; toggleClasses: string; onClick: ( - e: + _e: | React.MouseEvent | React.KeyboardEvent, ) => void; - onKeyDown: (e: React.KeyboardEvent) => void; - onFocus: (e: React.FocusEvent) => void; - onBlur: (e: React.FocusEvent) => void; + onKeyDown: (_e: React.KeyboardEvent) => void; + onFocus: (_e: React.FocusEvent) => void; + onBlur: (_e: React.FocusEvent) => void; } diff --git a/app/components/ToggleGroup/ToggleGroup.container.tsx b/app/components/ToggleGroup/ToggleGroup.container.tsx index a55e829..37b9182 100644 --- a/app/components/ToggleGroup/ToggleGroup.container.tsx +++ b/app/components/ToggleGroup/ToggleGroup.container.tsx @@ -119,7 +119,6 @@ const ToggleGroupContainer = memo( return ( + > + {children} + ); }), ); diff --git a/app/components/ToggleGroup/ToggleGroup.types.ts b/app/components/ToggleGroup/ToggleGroup.types.ts index 2ef113e..30fc2be 100644 --- a/app/components/ToggleGroup/ToggleGroup.types.ts +++ b/app/components/ToggleGroup/ToggleGroup.types.ts @@ -1,5 +1,7 @@ -export interface ToggleGroupProps - extends Omit, "onChange"> { +export interface ToggleGroupProps extends Omit< + React.ButtonHTMLAttributes, + "onChange" +> { children?: React.ReactNode; className?: string; position?: "left" | "middle" | "right"; @@ -24,8 +26,8 @@ export interface ToggleGroupViewProps { showText: boolean; ariaLabel?: string; toggleClasses: string; - onClick: (e: React.MouseEvent) => void; - onKeyDown: (e: React.KeyboardEvent) => void; - onFocus: (e: React.FocusEvent) => void; - onBlur: (e: React.FocusEvent) => void; + onClick: (_e: React.MouseEvent) => void; + onKeyDown: (_e: React.KeyboardEvent) => void; + onFocus: (_e: React.FocusEvent) => void; + onBlur: (_e: React.FocusEvent) => void; } diff --git a/app/components/ToggleGroup/ToggleGroup.view.tsx b/app/components/ToggleGroup/ToggleGroup.view.tsx index a6ecdfa..5daabec 100644 --- a/app/components/ToggleGroup/ToggleGroup.view.tsx +++ b/app/components/ToggleGroup/ToggleGroup.view.tsx @@ -3,9 +3,9 @@ import type { ToggleGroupViewProps } from "./ToggleGroup.types"; export function ToggleGroupView({ groupId, children, - className, - position, - state, + className: _className, + position: _position, + state: _state, showText, ariaLabel, toggleClasses, diff --git a/app/components/WebVitalsDashboard/WebVitalsDashboard.container.tsx b/app/components/WebVitalsDashboard/WebVitalsDashboard.container.tsx index ec2037e..0b9ea0c 100644 --- a/app/components/WebVitalsDashboard/WebVitalsDashboard.container.tsx +++ b/app/components/WebVitalsDashboard/WebVitalsDashboard.container.tsx @@ -96,11 +96,14 @@ const WebVitalsDashboardContainer = memo(() => { }, []); return ( - + ); }); WebVitalsDashboardContainer.displayName = "WebVitalsDashboard"; export default WebVitalsDashboardContainer; - diff --git a/app/components/WebVitalsDashboard/WebVitalsDashboard.types.ts b/app/components/WebVitalsDashboard/WebVitalsDashboard.types.ts index eae2f88..945bb7f 100644 --- a/app/components/WebVitalsDashboard/WebVitalsDashboard.types.ts +++ b/app/components/WebVitalsDashboard/WebVitalsDashboard.types.ts @@ -31,4 +31,3 @@ export interface WebVitalsDashboardViewProps { metrics: Metrics; loading: boolean; } - diff --git a/app/components/WebVitalsDashboard/WebVitalsDashboard.view.tsx b/app/components/WebVitalsDashboard/WebVitalsDashboard.view.tsx index b2e099a..ec1d073 100644 --- a/app/components/WebVitalsDashboard/WebVitalsDashboard.view.tsx +++ b/app/components/WebVitalsDashboard/WebVitalsDashboard.view.tsx @@ -111,9 +111,7 @@ function WebVitalsDashboardView({ Needs Improvement: {data.needsImprovementCount} - - Poor: {data.poorCount} - + Poor: {data.poorCount} @@ -155,4 +153,3 @@ function WebVitalsDashboardView({ } export default WebVitalsDashboardView; - diff --git a/app/components/WebVitalsDashboard/index.tsx b/app/components/WebVitalsDashboard/index.tsx index 88fae72..9c9d1b2 100644 --- a/app/components/WebVitalsDashboard/index.tsx +++ b/app/components/WebVitalsDashboard/index.tsx @@ -1,3 +1,2 @@ export { default } from "./WebVitalsDashboard.container"; export * from "./WebVitalsDashboard.types"; - diff --git a/app/hooks/useMediaQuery.ts b/app/hooks/useMediaQuery.ts index a04e921..8f6c134 100644 --- a/app/hooks/useMediaQuery.ts +++ b/app/hooks/useMediaQuery.ts @@ -33,27 +33,30 @@ export function useMediaQuery( query: string | keyof typeof BREAKPOINTS, direction: "min" | "max" = "min", ): boolean { - const [matches, setMatches] = useState(false); + // Convert breakpoint key to media query string + let mediaQuery: string; + if (query in BREAKPOINTS) { + const breakpoint = BREAKPOINTS[query as keyof typeof BREAKPOINTS]; + mediaQuery = `(${direction}-width: ${breakpoint}px)`; + } else { + mediaQuery = query; + } + + // Initialize state with current match if available (SSR safety) + const [matches, setMatches] = useState(() => { + if (typeof window === "undefined") { + return false; + } + return window.matchMedia(mediaQuery).matches; + }); useEffect(() => { - // Convert breakpoint key to media query string - let mediaQuery: string; - if (query in BREAKPOINTS) { - const breakpoint = BREAKPOINTS[query as keyof typeof BREAKPOINTS]; - mediaQuery = `(${direction}-width: ${breakpoint}px)`; - } else { - mediaQuery = query; - } - // Check if window is available (SSR safety) if (typeof window === "undefined") { return; } const media = window.matchMedia(mediaQuery); - // Initialize matches synchronously - this is safe for media queries - // eslint-disable-next-line react-hooks/rules-of-hooks - setMatches(media.matches); // Create listener for changes const listener = (event: MediaQueryListEvent) => { diff --git a/docs/guides/container-presentation-pattern.md b/docs/guides/container-presentation-pattern.md index 2d5807d..d1bb041 100644 --- a/docs/guides/container-presentation-pattern.md +++ b/docs/guides/container-presentation-pattern.md @@ -16,6 +16,7 @@ The Container/Presentation pattern separates component logic from presentation, ### When to Use Use this pattern for components that have: + - Business logic or state management - Data fetching or API calls - Analytics tracking @@ -40,6 +41,7 @@ app/components/[ComponentName]/ ### File Responsibilities #### `index.tsx` + - Exports the container component as the default export - Optionally exports types for external use - Maintains backward compatibility with existing import paths @@ -50,7 +52,9 @@ export type { AskOrganizerProps } from "./AskOrganizer.types"; ``` #### `[ComponentName].container.tsx` + **Contains all logic:** + - React hooks (`useState`, `useEffect`, custom hooks) - Event handlers and business logic - Data fetching and API calls @@ -60,6 +64,7 @@ export type { AskOrganizerProps } from "./AskOrganizer.types"; - Side effects **Should NOT contain:** + - JSX layout details (beyond composing the view) - Inline styles or complex className logic (pass as props) - Direct DOM manipulation @@ -74,7 +79,7 @@ import type { AskOrganizerProps } from "./AskOrganizer.types"; function AskOrganizerContainer(props: AskOrganizerProps) { const { trackEvent } = useAnalytics(); - + const handleContactClick = () => { trackEvent({ event: "contact_button_click", @@ -83,10 +88,10 @@ function AskOrganizerContainer(props: AskOrganizerProps) { }); // ... additional logic }; - + // Compute derived props const variantStyles = computeVariantStyles(props.variant); - + return ( { const { trackEvent } = useAnalytics(); - + const handleContactClick = () => { trackEvent({ event: "contact_click", component: "AskOrganizer" }); }; - + return (
@@ -237,6 +250,7 @@ const AskOrganizer = memo(({ title, variant, ...props }) => { ### After (Container/Presentation) **AskOrganizer.container.tsx:** + ```typescript "use client"; @@ -247,11 +261,11 @@ import type { AskOrganizerProps } from "./AskOrganizer.types"; function AskOrganizerContainer(props: AskOrganizerProps) { const { trackEvent } = useAnalytics(); - + const handleContactClick = () => { trackEvent({ event: "contact_click", component: "AskOrganizer" }); }; - + return ; } @@ -259,6 +273,7 @@ export default memo(AskOrganizerContainer); ``` **AskOrganizer.view.tsx:** + ```typescript import ContentLockup from "../ContentLockup"; import Button from "../Button"; @@ -346,6 +361,7 @@ These components serve as reference implementations for the pattern. The following components are candidates for future conversion: ### High Priority (Complex Logic) + - `Header` / `HomeHeader` - Navigation state, conditional rendering logic - `MenuBar` - Menu state management, keyboard navigation - `ContextMenu` - Positioning logic, click outside handling @@ -353,12 +369,14 @@ The following components are candidates for future conversion: - `ToggleGroup` - Group state management ### Medium Priority (Some Logic) + - `ContentContainer` - Data fetching or transformation - `RelatedArticles` - Data fetching, filtering logic - `RuleStack` - Complex rendering logic - `LogoWall` - Animation or interaction logic ### Low Priority (Mostly Presentational) + - `Button`, `Avatar`, `Checkbox`, `Input`, `TextArea` - Simple presentational components - `Separator`, `SectionHeader`, `SectionNumber` - Pure presentation - `QuoteBlock`, `QuoteDecor`, `HeroDecor` - Decorative components diff --git a/scripts/test-local.sh b/scripts/test-local.sh index 702a244..af515de 100755 --- a/scripts/test-local.sh +++ b/scripts/test-local.sh @@ -3,9 +3,37 @@ # Local testing script - run before committing/merging # Usage: ./scripts/test-local.sh +set -euo pipefail # Exit on error, undefined vars, pipe failures + echo "๐Ÿงช Running local tests before commit..." echo "" +# Cleanup function to ensure servers are killed +cleanup() { + echo "" + echo "๐Ÿงน Cleaning up any running servers..." + # Kill any Next.js servers on common test ports + for port in 3000 3010; do + if lsof -ti:$port >/dev/null 2>&1; then + echo " Killing process on port $port..." + lsof -ti:$port | xargs kill -9 2>/dev/null || true + fi + done + # Kill any processes from PID files if they exist + for pidfile in .next/runner.pid .next/performance-server.pid; do + if [ -f "$pidfile" ]; then + PID=$(cat "$pidfile" 2>/dev/null || echo "") + if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then + echo " Killing server PID: $PID (from $pidfile)..." + kill -9 "$PID" 2>/dev/null || true + fi + rm -f "$pidfile" + fi + done +} + +trap cleanup EXIT INT TERM + echo "๐Ÿ” Linting..." npm run lint || exit 1 @@ -23,11 +51,42 @@ npm run test:e2e || exit 1 echo "" echo "๐Ÿ–ผ๏ธ Visual regression tests..." +# Visual tests use Playwright's webServer config, should auto-start server npm run visual:test || exit 1 echo "" echo "โšก Performance tests (Lighthouse CI)..." -npm run performance:budget || exit 1 +# Performance tests need a server running on port 3010 +# Check if server is already running, if not start one +if ! lsof -ti:3010 >/dev/null 2>&1; then + echo " Starting server for performance tests..." + npm run build || exit 1 + PORT=3010 HOST=127.0.0.1 node node_modules/next/dist/bin/next start -p 3010 -H 127.0.0.1 > .next/performance-server.log 2>&1 & + SERVER_PID=$! + echo "$SERVER_PID" > .next/performance-server.pid + echo " Server started (PID: $SERVER_PID), waiting for readiness..." + npx wait-on -t 120000 "tcp:127.0.0.1:3010" || { echo "โŒ Server failed to start"; kill $SERVER_PID 2>/dev/null || true; exit 1; } + echo " โœ… Server ready" +fi + +# Run performance tests +npm run performance:budget || EXIT_CODE=$? + +# Cleanup performance server if we started it +if [ -f .next/performance-server.pid ]; then + PID=$(cat .next/performance-server.pid 2>/dev/null || echo "") + if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then + echo " Stopping performance test server..." + kill "$PID" 2>/dev/null || true + sleep 2 + kill -9 "$PID" 2>/dev/null || true + fi + rm -f .next/performance-server.pid +fi + +if [ -n "${EXIT_CODE:-}" ]; then + exit $EXIT_CODE +fi echo "" echo "โœ… All tests passed! Safe to commit/merge."