Repository: PhillipsAuctionHouse/seldon
Pull request #546: feat(Input): L3-5705 added skeleton
Author: login: constantinehuzenko
Labels: name: released
Created: 2025-03-10T18:22:33Z | Merged: 2025-03-11T19:04:14Z

**Jira ticket**

[L3-5705](https://phillipsauctions.atlassian.net/browse/L3-5705)

![Screenshot 2025-03-10 at 2 17 26 PM](https://github.com/user-attachments/assets/fd6a45cd-1ea4-46ed-8fc7-5eca8c448427)
![image](https://github.com/user-attachments/assets/1194ebf4-3e44-490a-a65a-9e7cd37402bc)
![image](https://github.com/user-attachments/assets/d9006385-aa7d-4652-aa62-7665951cd0eb)

**Summary**

Added skeleton for button, input and text

**Change List (describe the changes made to the files)**

- Additions, Changes, and Removals. (You can use [GitHub Copilot PR Summary](https://docs.github.com/en/enterprise-cloud@latest/copilot/using-github-copilot/using-github-copilot-for-pull-requests/creating-a-pull-request-summary-with-github-copilot) to populate this section)
This pull request introduces a skeleton loading state to various components, enhancing the user experience by providing visual feedback while content is being loaded. The changes include updates to the `Button`, `Input`, and `Text` components, as well as their corresponding tests and stories.

### Skeleton Loading State Implementation:

* **Button Component:**
  - Added `isSkeletonLoading` prop to display skeleton loader.
  - Updated class names to include skeleton loader class when `isSkeletonLoading` is true.
  - Added tests to verify the skeleton loader functionality.

* **Input Component:**
  - Added `isSkeletonLoading` prop to display skeleton loader.
  - Updated class names to include skeleton loader class when `isSkeletonLoading` is true.
  - Added tests to verify the skeleton loader functionality.

* **Text Component:**
  - Added `isSkeletonLoading` prop to display skeleton loader.
  - Wrapped text content in a `span` with skeleton loader class when `isSkeletonLoading` is true.
  - Added tests to verify the skeleton loader functionality.

### Storybook Updates:

* **Button Stories:**
  - Added `ButtonWithSkeleton` story to demonstrate the skeleton loading state.

* **Input Stories:**
  - Updated `Playground` story to include skeleton loading state for various input types.

* **Text Stories:**
  - Added `TextWithSkeleton` story to demonstrate the skeleton loading state.

### SCSS Updates:

* **Shared Classes:**
  - Added skeleton loader styles to `_sharedClasses.scss`, including keyframes for skeleton pulse animation.

These changes collectively enhance the visual feedback provided to users during content loading, ensuring a smoother and more responsive user experience.

**Acceptance Test (how to verify the PR)**

- go to button component and verify that we have skeleton and nothing is broken
- go to input component and verify that we have skeleton and nothing is broken
- go to text component and verify that we have skeleton and nothing is broken

**Regression Test**

- (Optional) Add verification steps to make sure this PR doesn't break old functionality

**Evidence of testing**

- Post logs, screenshots, etc

<!-- For reviewers: do not remove -->

**Things to look for during review**

- [ ] PR title should correctly describe the most significant type of commit. I.e. `feat(scope): ...` if a `minor` release should be triggered.
- [ ] All commit messages follow convention and are appropriate for the changes
- [ ] All references to `phillips` class prefix are using the prefix variable
- [ ] All major areas have a `data-testid` attribute.
- [ ] Document all props with jsdoc comments
- [ ] All strings should be translatable.
- [ ] Unit tests should be written and should have a coverage of 90% or higher in all areas.

New Components

- [ ] Are there any [accessibility considerations](https://www.w3.org/WAI/ARIA/apg/patterns/) that need to be taken into account and tested?
- [ ] Default story called "Playground" should be created for all new components
- [ ] Create a jsdoc comment that has an Overview section and a link to the Figma design for the component
- [ ] Export the component and its typescript type from the `index.ts` file
- [ ] Import the component scss file into the `componentStyles.scss` file.


[L3-5705]: https://phillipsauctions.atlassian.net/browse/L3-5705?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ


Diff:
diff --git a/src/components/Article/Article.test.tsx b/src/components/Article/Article.test.tsx
index b70e3f1ed..ca728dd1b 100644
--- a/src/components/Article/Article.test.tsx
+++ b/src/components/Article/Article.test.tsx
@@ -23,7 +23,7 @@ describe('Article', () => {
     expect(screen.getByText('Test Label')).toBeInTheDocument();
     expect(screen.getByText('Test Header')).toBeInTheDocument();
     expect(screen.getByText('Test Description')).toBeInTheDocument();
-    expect(screen.getByText('Test Link').parentElement).toHaveAttribute('href', 'https://example.com');
+    expect(screen.getByText('Test Link').parentElement?.parentElement).toHaveAttribute('href', 'https://example.com');
   });
 
   it('renders correctly without optional props', () => {
diff --git a/src/components/Button/Button.stories.tsx b/src/components/Button/Button.stories.tsx
index c277fdc15..fa9a01d7b 100644
--- a/src/components/Button/Button.stories.tsx
+++ b/src/components/Button/Button.stories.tsx
@@ -52,10 +52,28 @@ ButtonAsLinkWithPrefetch.args = {
   size: 'md',
 };
 
+export const ButtonWithSkeleton = (props: ButtonProps) => (
+  <div style={{ display: 'flex', gap: '1rem' }}>
+    <Button {...props} isSkeletonLoading>
+      Visit Phillips
+    </Button>
+    <Button {...props}>Visit Phillips</Button>
+  </div>
+);
+
+ButtonAsLinkWithPrefetch.args = {
+  variant: ButtonVariants.tertiary,
+  size: 'md',
+};
+
 // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
 export const Playground = {
   args: {
     children: 'Button',
     variant: ButtonVariants.primary,
+    isSkeletonLoading: false,
+  },
+  argTypes: {
+    isSkeletonLoading: { control: 'boolean' },
   },
 };
diff --git a/src/components/Button/Button.test.tsx b/src/components/Button/Button.test.tsx
index 22a0038e1..73323be3e 100644
--- a/src/components/Button/Button.test.tsx
+++ b/src/components/Button/Button.test.tsx
@@ -17,6 +17,7 @@ describe('Button', () => {
     expect(buttonElement).toHaveClass(`${px}-button`);
     expect(buttonElement).toHaveClass(`${px}-button--primary`);
     expect(buttonElement).not.toHaveClass(`${px}-button--icon-last`);
+    expect(buttonElement).not.toHaveClass(`${px}-skeleton`);
   });
 
   it('renders with type and size classnames', () => {
@@ -94,4 +95,10 @@ describe('Button', () => {
       expect(screen.getByTestId('modulepreload-link')).toBeInTheDocument();
     });
   });
+
+  it('should render with skeleton class name', () => {
+    render(<Button isSkeletonLoading>Submit</Button>);
+    const buttonElement = screen.getByText('Submit');
+    expect(buttonElement).toHaveClass(`${px}-skeleton`);
+  });
 });
diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx
index 2241c1984..27198c668 100644
--- a/src/components/Button/Button.tsx
+++ b/src/components/Button/Button.tsx
@@ -1,5 +1,5 @@
 import classnames from 'classnames';
-import { getCommonProps } from '../../utils';
+import { getCommonProps, px } from '../../utils';
 import { ButtonVariants } from './types';
 import { forwardRef, useState } from 'react';
 
@@ -40,6 +40,11 @@ export interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement | HT
    * prefetch the link
    */
   prefetch?: 'none' | 'intent' | 'render' | 'viewport';
+
+  /**
+   * Boolean to specify whether we need to display skeleton loader
+   */
+  isSkeletonLoading?: boolean;
 }
 
 /**
@@ -64,6 +69,7 @@ const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonProps>(
       href,
       target,
       prefetch = 'none',
+      isSkeletonLoading,
       ...props
     },
     ref,
@@ -114,6 +120,7 @@ const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonProps>(
             `${baseClassName}--${variant}`,
             {
               [`${baseClassName}--icon-last`]: iconLast,
+              [`${px}-skeleton`]: isSkeletonLoading,
             },
             className,
           )}
diff --git a/src/components/Input/Input.stories.tsx b/src/components/Input/Input.stories.tsx
index 0e28192b8..fca6ae787 100644
--- a/src/components/Input/Input.stories.tsx
+++ b/src/components/Input/Input.stories.tsx
@@ -2,6 +2,7 @@ import type { Meta } from '@storybook/react';
 import Input, { InputProps } from './Input';
 import { useState } from 'react';
 import Button from '../Button/Button';
+import { Text, TextVariants } from '../Text';
 // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
 const meta = {
   title: 'Components/Input',
@@ -239,6 +240,7 @@ CustomLabel.args = {
 CustomLabel.argTypes = {};
 
 export const Playground = ({ playgroundWidth, ...args }: StoryProps) => {
+  const [loading, setLoading] = useState(false);
   const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
     e.preventDefault();
     const formData = new FormData(e.currentTarget);
@@ -251,12 +253,46 @@ export const Playground = ({ playgroundWidth, ...args }: StoryProps) => {
 
   return (
     <div style={{ width: playgroundWidth, margin: '1rem' }}>
+      <Text variant={TextVariants.heading2} isSkeletonLoading={loading}>
+        Form Inputs
+      </Text>
       <form onSubmit={handleSubmit}>
-        <Input {...args} labelText="Text Input" id="Input-1" name="stringInput" />
-        <Input type="checkbox" {...args} labelText="Checkbox Input" id="Input-2" name="checkboxInput" />
-        <Input type="radio" {...args} id="Input-3" labelText="Radio Input" name="radioInput" />
-        <Input type="toggle" {...args} id="Input-4" labelText="Toggle Input" name="toggleInput" />
-        <Button type="submit">Submit form</Button>
+        <Input {...args} labelText="Text Input" id="Input-1" name="stringInput" isSkeletonLoading={loading} />
+        <Input
+          type="checkbox"
+          {...args}
+          labelText="Checkbox Input"
+          id="Input-2"
+          name="checkboxInput"
+          isSkeletonLoading={loading}
+        />
+        <Input
+          type="radio"
+          {...args}
+          id="Input-3"
+          labelText="Radio Input"
+          name="radioInput"
+          isSkeletonLoading={loading}
+        />
+        <Input
+          type="toggle"
+          {...args}
+          id="Input-4"
+          labelText="Toggle Input"
+          name="toggleInput"
+          isSkeletonLoading={loading}
+        />
+        <Text variant={TextVariants.body1} isSkeletonLoading={loading}>
+          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam eget nibh ac turpis molestie ultricies. Morbi
+          placerat rhoncus elit, sed malesuada ante rutrum et.
+        </Text>
+
+        <div style={{ display: 'flex', width: '100%', justifyContent: 'space-between', gap: '1rem' }}>
+          <Button type="submit" isSkeletonLoading={loading}>
+            Submit
+          </Button>
+          <Button onClick={() => setLoading((prev) => !prev)}>Loading</Button>
+        </div>
       </form>
     </div>
   );
diff --git a/src/components/Input/Input.test.tsx b/src/components/Input/Input.test.tsx
index 4a4c0cd2e..135c9159a 100644
--- a/src/components/Input/Input.test.tsx
+++ b/src/components/Input/Input.test.tsx
@@ -3,6 +3,7 @@ import userEvent from '@testing-library/user-event';
 import * as React from 'react';
 
 import Input, { InputProps } from './Input';
+import { px } from '../../utils';
 
 // NOTE: When using default value on controlled input we pass to the state hook and not the input
 const TestInput = React.forwardRef(
@@ -154,4 +155,16 @@ describe('An Input', () => {
     const spanElement = screen.getByText('Test Span');
     expect(spanElement).toBeInTheDocument();
   });
+
+  it('should render with skeleton class name', () => {
+    render(<Input labelText="Text Input" id="Input-1" name="stringInput" isSkeletonLoading />);
+    const inputElement = screen.getByRole('textbox');
+    expect(inputElement).toHaveClass(`${px}-skeleton`);
+  });
+
+  it('should not render with skeleton class name', () => {
+    render(<Input labelText="Text Input" id="Input-1" name="stringInput" />);
+    const inputElement = screen.getByRole('textbox');
+    expect(inputElement).not.toHaveClass(`${px}-skeleton`);
+  });
 });
diff --git a/src/components/Input/Input.tsx b/src/components/Input/Input.tsx
index 96d86f667..735b29718 100644
--- a/src/components/Input/Input.tsx
+++ b/src/components/Input/Input.tsx
@@ -95,6 +95,11 @@ export interface InputProps extends Omit<React.ComponentProps<'input'>, 'size'>
    * Text that is displayed when the control is in warning state
    */
   warnText?: React.ReactNode;
+
+  /**
+   * Boolean to specify whether we need to display skeleton loader
+   */
+  isSkeletonLoading?: boolean;
 }
 
 /**
@@ -132,6 +137,7 @@ const Input = React.forwardRef(
       value,
       warn,
       warnText,
+      isSkeletonLoading,
       ...rest
     }: InputProps,
     ref: React.ForwardedRef<HTMLInputElement>,
@@ -162,18 +168,23 @@ const Input = React.forwardRef(
         <label
           data-testid={`label-${id || generatedId}`}
           htmlFor={id || generatedId}
-          className={classnames(`${px}-input__label`, { [`${px}-input__label--hidden`]: hideLabel })}
+          className={classnames(`${px}-input__label`, {
+            [`${px}-input__label--hidden`]: hideLabel,
+            [`${px}-skeleton`]: isSkeletonLoading,
+          })}
         >
           {labelText}
         </label>
         <input
-          className={classnames(`${px}-input__input`, className)}
+          className={classnames(`${px}-input__input`, className, {
+            [`${px}-skeleton`]: isSkeletonLoading,
+          })}
           data-testid={id}
           disabled={inputProps.disabled}
           id={id || generatedId}
           onChange={onChange}
           onClick={onClick}
-          placeholder={placeholder}
+          placeholder={isSkeletonLoading ? '' : placeholder}
           readOnly={readOnly}
           ref={ref}
           type={inputProps.type}
diff --git a/src/components/Input/_input.scss b/src/components/Input/_input.scss
index 57ff00cbc..e02486e4b 100644
--- a/src/components/Input/_input.scss
+++ b/src/components/Input/_input.scss
@@ -19,6 +19,7 @@ $lg: #{$px}-input--lg;
     color: $pure-black;
     font-variation-settings: 'wght' 600;
     margin-bottom: 0.5rem;
+    width: fit-content;
     word-break: break-word;
 
     &--hidden {
diff --git a/src/components/Text/Text.stories.tsx b/src/components/Text/Text.stories.tsx
index c293564b2..6c7ac8273 100644
--- a/src/components/Text/Text.stories.tsx
+++ b/src/components/Text/Text.stories.tsx
@@ -31,6 +31,33 @@ Playground.argTypes = {
   },
 };
 
+export const TextWithSkeleton = (props: TextProps) => <Text {...props} style={{ maxWidth: '300px' }} />;
+
+TextWithSkeleton.args = {
+  children:
+    'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam eget nibh ac turpis molestie ultricies. Morbi placerat rhoncus elit, sed malesuada ante rutrum et.',
+  variant: TextVariants.body2,
+  isSkeletonLoading: true,
+};
+
+TextWithSkeleton.argTypes = {
+  variant: {
+    options: TextVariants,
+    control: {
+      type: 'select',
+    },
+  },
+  align: {
+    options: TextAlignments,
+    control: {
+      type: 'select',
+    },
+  },
+  isSkeletonLoading: {
+    control: 'boolean',
+  },
+};
+
 export const SuperScript = (props: TextProps) => <Text variant={TextVariants.heading3} {...props} />;
 
 SuperScript.args = {
diff --git a/src/components/Text/Text.test.tsx b/src/components/Text/Text.test.tsx
index 7f01cffe6..feb2cd9a4 100644
--- a/src/components/Text/Text.test.tsx
+++ b/src/components/Text/Text.test.tsx
@@ -38,19 +38,19 @@ describe('Text', () => {
   it('applies the default variant correctly', () => {
     renderText({ children: 'Default Variant' });
 
-    expect(screen.getByText('Default Variant')).toHaveClass(`${px}-text--body2`);
+    expect(screen.getByText('Default Variant').parentElement).toHaveClass(`${px}-text--body2`);
   });
 
   it('applies the custom variant correctly', () => {
     renderText({ children: 'Custom Variant', variant: TextVariants.body1 });
 
-    expect(screen.getByText('Custom Variant')).toHaveClass(`${px}-text--body1`);
+    expect(screen.getByText('Custom Variant').parentElement).toHaveClass(`${px}-text--body1`);
   });
 
   it('applies additional className correctly', () => {
     renderText({ children: 'Additional Class', className: 'custom-class' });
 
-    expect(screen.getByText('Additional Class')).toHaveClass('custom-class');
+    expect(screen.getByText('Additional Class').parentElement).toHaveClass('custom-class');
   });
 
   it('renders with custom element correctly', () => {
@@ -59,4 +59,16 @@ describe('Text', () => {
     expect(screen.getByText('Custom Element')).toBeInTheDocument();
     expect(screen.getByText('Custom Element').tagName).toBe('SPAN');
   });
+
+  it('should render with skeleton class name', () => {
+    render(<Text isSkeletonLoading>Test</Text>);
+    const textElement = screen.getByText('Test');
+    expect(textElement).toHaveClass(`${px}-skeleton`);
+  });
+
+  it('should not render with skeleton class name', () => {
+    render(<Text>Test</Text>);
+    const textElement = screen.getByText('Test');
+    expect(textElement).not.toHaveClass(`${px}-skeleton`);
+  });
 });
diff --git a/src/components/Text/Text.tsx b/src/components/Text/Text.tsx
index 6c285be03..4c7c371f0 100644
--- a/src/components/Text/Text.tsx
+++ b/src/components/Text/Text.tsx
@@ -1,5 +1,5 @@
 import React from 'react';
-import { getCommonProps } from '../../utils';
+import { getCommonProps, px } from '../../utils';
 import { TextAlignments, TextVariants } from './types';
 import { determineDefaultTextElement, determineTextClassName } from './utils';
 import classNames from 'classnames';
@@ -17,6 +17,10 @@ export interface TextProps extends React.HTMLAttributes<HTMLElement> {
    * The alignment of the text
    */
   align?: TextAlignments;
+  /**
+   * Boolean to specify whether we need to display skeleton loader
+   */
+  isSkeletonLoading?: boolean;
 }
 /**
  * ## Overview
@@ -33,6 +37,7 @@ const Text = ({
   element: CustomElement,
   variant = TextVariants.body2,
   align,
+  isSkeletonLoading,
   ...props
 }: TextProps) => {
   const Component = CustomElement || determineDefaultTextElement(variant);
@@ -46,7 +51,13 @@ const Text = ({
       })}
       {...props}
     >
-      {children}
+      <span
+        className={classNames({
+          [`${px}-skeleton`]: isSkeletonLoading,
+        })}
+      >
+        {children}
+      </span>
     </Component>
   );
 };
diff --git a/src/patterns/HeroBanner/HeroBanner.test.tsx b/src/patterns/HeroBanner/HeroBanner.test.tsx
index 335a26f4a..330bdfb84 100644
--- a/src/patterns/HeroBanner/HeroBanner.test.tsx
+++ b/src/patterns/HeroBanner/HeroBanner.test.tsx
@@ -16,7 +16,7 @@ describe('HeroBanner', () => {
   it('Renders the header when headerText is passed in', () => {
     render(<HeroBanner headerText="This is my text" />);
     // Should normally not be testing implementation details. Should try to focus test on the user interaction and rendering UI that the user sees e.g. toBeInTheDocument
-    expect(screen.getByText(/This is my text/).nodeName).toEqual('H1');
+    expect(screen.getByText(/This is my text/)?.parentElement?.nodeName).toEqual('H1');
     expect(screen.getByText(/This is my text/)).toBeInTheDocument();
   });
 
diff --git a/src/scss/_sharedClasses.scss b/src/scss/_sharedClasses.scss
index 87d6d7b9f..e58fb0ba7 100644
--- a/src/scss/_sharedClasses.scss
+++ b/src/scss/_sharedClasses.scss
@@ -1,3 +1,5 @@
+/* stylelint-disable order/properties-alphabetical-order */
+/* stylelint-disable declaration-empty-line-before */
 @use './vars' as *;
 @use './utils' as *;
 
@@ -9,3 +11,61 @@
     }
   }
 }
+
+.#{$px}-skeleton,
+.#{$px}-skeleton::before,
+.#{$px}-skeleton::after {
+  animation: skeleton-pulse 1s infinite alternate-reverse !important;
+  background-image: none !important;
+  background-clip: initial !important;
+  border-radius: 0.1875rem !important;
+
+  border-color: #0000 !important;
+  box-shadow: none !important;
+  /* stylelint-disable-next-line property-no-vendor-prefix */
+  -webkit-box-decoration-break: clone !important;
+  box-decoration-break: clone !important;
+  color: #0000 !important;
+  outline: none !important;
+  pointer-events: none !important;
+  /* stylelint-disable-next-line property-no-vendor-prefix */
+  -webkit-user-select: none !important;
+  user-select: none !important;
+  cursor: default !important;
+}
+
+input[type='checkbox'].#{$px}-skeleton,
+input[type='radio'].#{$px}-skeleton {
+  /* stylelint-disable-next-line property-no-vendor-prefix */
+  -webkit-appearance: none;
+  /* stylelint-disable-next-line property-no-vendor-prefix */
+  -moz-appearance: none;
+  appearance: none;
+  padding: 0 !important;
+  width: 13px !important;
+  height: 13px !important;
+}
+
+button.#{$px}-skeleton {
+  border-radius: 6.25rem !important;
+}
+
+.#{$px}-toggle-input {
+  .#{$px}-skeleton::before,
+  .#{$px}-skeleton::after {
+    border-radius: 6.25rem !important;
+  }
+  .#{$px}-skeleton::after {
+    display: none !important;
+  }
+}
+
+@keyframes skeleton-pulse {
+  0% {
+    background-color: #0000000f;
+  }
+
+  100% {
+    background-color: #0000001f;
+  }
+}


Reviews:
user: login: adietrich3074
body: LGTM 👍🏼
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T18:42:56Z
user: login: sgrund14
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T19:00:34Z
user: login: sgrund14
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T19:02:38Z
user: login: sgrund14
body: lgtm but why do we need those random test updates
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T19:02:51Z

Review comments:
diff_hunk: @@ -23,7 +23,7 @@ describe('Article', () => {
     expect(screen.getByText('Test Label')).toBeInTheDocument();
     expect(screen.getByText('Test Header')).toBeInTheDocument();
     expect(screen.getByText('Test Description')).toBeInTheDocument();
-    expect(screen.getByText('Test Link').parentElement).toHaveAttribute('href', 'https://example.com');
+    expect(screen.getByText('Test Link').parentElement?.parentElement).toHaveAttribute('href', 'https://example.com');
path: src/components/Article/Article.test.tsx
user: login: sgrund14
body: ?
created_at: 2025-03-11T19:00:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -16,7 +16,7 @@ describe('HeroBanner', () => {
   it('Renders the header when headerText is passed in', () => {
     render(<HeroBanner headerText="This is my text" />);
     // Should normally not be testing implementation details. Should try to focus test on the user interaction and rendering UI that the user sees e.g. toBeInTheDocument
-    expect(screen.getByText(/This is my text/).nodeName).toEqual('H1');
+    expect(screen.getByText(/This is my text/)?.parentElement?.nodeName).toEqual('H1');
path: src/patterns/HeroBanner/HeroBanner.test.tsx
user: login: sgrund14
body: ?
created_at: 2025-03-11T19:02:37Z
author_association: CONTRIBUTOR

PR comments:
user: login: netlify[bot]
created_at: 2025-03-10T18:22:47Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *phillips-seldon* ready!


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | c691084e2c2ae459adddc86c9854d531b636065f |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/phillips-seldon/deploys/67cf2decbe1dcc00089d0d51 |
|<span aria-hidden="true">😎</span> Deploy Preview | [https://deploy-preview-546--phillips-seldon.netlify.app](https://deploy-preview-546--phillips-seldon.netlify.app) |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTU0Ni0tcGhpbGxpcHMtc2VsZG9uLm5ldGxpZnkuYXBwIn0.t75fOCEmfPBc056sL8Ldht2NzfANTqDAzAB8fE-SKPU)<br /><br />_Use your smartphone camera to open QR code link._</details> |
---
<!-- [phillips-seldon Preview](https://deploy-preview-546--phillips-seldon.netlify.app) -->
_To edit notification comments on pull requests, go to your [Netlify site configuration](https://app.netlify.com/sites/phillips-seldon/configuration/notifications#deploy-webhooks)._
author_association: NONE
user: login: davidicus
created_at: 2025-03-11T19:05:40Z
body: :tada: This issue has been resolved in version 1.125.0 :tada:

The release is available on:
- [GitHub release](https://github.com/PhillipsAuctionHouse/seldon/releases/tag/v1.125.0)
- [npm package (@latest dist-tag)](https://www.npmjs.com/package/@phillips/seldon/v/1.125.0)

Your **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package::rocket:
author_association: CONTRIBUTOR