Skip to content

Commit

Permalink
Feature: FitText (#1342)
Browse files Browse the repository at this point in the history
Add `FitText` component.
  • Loading branch information
ryan-roemer authored Dec 13, 2024
1 parent c31b26a commit 07667fc
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/fuzzy-tips-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'spectacle': minor
---

Add `FitText` typography component.
1 change: 1 addition & 0 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ These tags are for displaying textual content.
| Tag Name | Theme Props | Additional Props | Default Props |
|---------------------|-------------------------------------------------------------------------------------------------------------|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`Text`** | [**Space**](./props#space)<br />[**Color**](./props#color)<br /> [**Typography**](./props#typography) || **color**: primary<br /> **fontFamily**: text<br />**fontSize**: text<br />**textAlign**: left<br />**margin**: textMargin |
| **`FitText`** | [**Space**](./props#space)<br />[**Color**](./props#color)<br /> [**Typography**](./props#typography) || **color**: primary<br /> **fontFamily**: text<br />**fontSize**: text<br />**textAlign**: center<br />**margin**: textMargin |
| **`Heading`** | [**Space**](./props#space)<br />[**Color**](./props#color)<br /> [**Typography**](./props#typography) || **color**: secondary<br /> **fontFamily**: header<br />**fontSize**: h1<br />**fontWeight**: bold<br />**textAlign**: center<br />**margin**: headerMargin |
| **`Link`** | [**Space**](./props#space)<br />[**Color**](./props#color)<br /> [**Typography**](./props#typography)<br /> | **href**: PropTypes.string | **color**: quaternary<br /> **fontFamily**: text<br />**fontSize**: text<br />**textDecoration**: underline<br />**textAlign**: left<br />**margin**: textMargin |
| **`Quote`** | [**Space**](./props#space)<br />[**Color**](./props#color)<br /> [**Typography**](./props#typography)<br /> || **color**: primary<br /> **fontFamily**: text<br />**fontSize**: text<br />**textAlign**: left<br />**borderLeft**: 1px solid secondary |
Expand Down
14 changes: 14 additions & 0 deletions examples/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
Slide,
Deck,
Text,
FitText,
Grid,
Box,
Image,
Expand Down Expand Up @@ -138,6 +139,19 @@ const Presentation = () => (
</Appear>
</OrderedList>
</Slide>
<Slide>
<Heading>This is a Heading</Heading>
<FitText>
This is a <CodeSpan>FitText</CodeSpan> component
</FitText>
<FitText
color="secondary"
style={{ textTransform: 'uppercase', fontFamily: 'Comic Sans MS' }}
>
Shorter fit text
</FitText>
<Text>This is a Text. (Resize this window!)</Text>
</Slide>
<Slide>
<FlexBox>
<Text>These</Text>
Expand Down
10 changes: 10 additions & 0 deletions examples/one-page/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
Slide,
Deck,
Text,
FitText,
Grid,
Box,
Image,
Expand Down Expand Up @@ -170,6 +171,15 @@
</${Appear}>
</${OrderedList}>
</${Slide}>
<${Slide}>
<${Heading}>This is a Heading</${Heading}>
<${FitText}>This is a <${CodeSpan}>FitText</${CodeSpan}> component</${FitText}>
<${FitText} color="secondary" style=${{
textTransform: 'uppercase',
fontFamily: 'Comic Sans MS'
}}>Shorter fit text</${FitText}>
<${Text}>This is a Text. (Resize this window!)</${Text}>
</${Slide}>
<${Slide}>
<${FlexBox}>
<${Text}>These</${Text}>
Expand Down
14 changes: 14 additions & 0 deletions examples/typescript/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
Slide,
Deck,
Text,
FitText,
Grid,
Box,
Image,
Expand Down Expand Up @@ -137,6 +138,19 @@ const Presentation = () => (
</Appear>
</OrderedList>
</Slide>
<Slide>
<Heading>This is a Heading</Heading>
<FitText>
This is a <CodeSpan>FitText</CodeSpan> component
</FitText>
<FitText
color="secondary"
style={{ textTransform: 'uppercase', fontFamily: 'Comic Sans MS' }}
>
Shorter fit text
</FitText>
<Text>This is a Text. (Resize this window!)</Text>
</Slide>
<Slide>
<FlexBox>
<Text>These</Text>
Expand Down
32 changes: 31 additions & 1 deletion packages/spectacle/src/components/typography.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
UnorderedList,
ListItem,
Link,
CodeSpan
CodeSpan,
FitText
} from './typography';
import { render } from '@testing-library/react';

Expand Down Expand Up @@ -91,3 +92,32 @@ describe('<CodeSpan />', () => {
expect(container.querySelector('code')?.innerHTML).toBe('Code!');
});
});

describe('<FitText />', () => {
beforeEach(() => {
// Default mock implementation
jest.mock('use-resize-observer', () => {
return { width: 500, height: 100 };
});
});

afterEach(() => {
jest.clearAllMocks();
});

it('should render text content correctly', () => {
const { getByText } = mountWithTheme(<FitText>Spectacle!</FitText>);
expect(getByText('Spectacle!')).toBeInTheDocument();
});

it('should apply color and typography props correctly', () => {
const { getByText } = mountWithTheme(
<FitText color="secondary" fontSize="h1">
Spectacle!
</FitText>
);
const textElement = getByText('Spectacle!');
expect(textElement).toHaveStyle({ color: defaultTheme.colors.secondary });
expect(textElement).toHaveStyle({ fontSize: 'h1' });
});
});
61 changes: 59 additions & 2 deletions packages/spectacle/src/components/typography.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ import {
SpaceProps,
BorderProps
} from 'styled-system';
import { FC, PropsWithChildren } from 'react';
import {
FC,
PropsWithChildren,
RefAttributes,
useRef,
useState,
HTMLAttributes
} from 'react';
import useResizeObserver from 'use-resize-observer';

const decoration = system({ textDecoration: true });
type DecorationProps = Pick<CSSObject, 'textDecoration'>;
Expand Down Expand Up @@ -113,6 +121,54 @@ ListItem.defaultProps = {
margin: 0
};

const FitContainer = styled.div`
width: 100%;
display: flex;
align-items: center;
justify-content: center;
`;

const ScalableText = styled(
Text as FC<CommonTypographyProps & RefAttributes<HTMLDivElement>>
)<{ scale: number }>`
transform-origin: center;
transform: scale(${(props) => props.scale});
white-space: nowrap;
`;
ScalableText.defaultProps = {
...Text.defaultProps,
textAlign: 'center',
scale: 1
};

const FitText: FC<
PropsWithChildren<CommonTypographyProps & HTMLAttributes<HTMLDivElement>>
> = (props) => {
const containerRef = useRef<HTMLDivElement>(null);
const textRef = useRef<HTMLDivElement>(null);
const [scale, setScale] = useState(1);

useResizeObserver({
ref: containerRef,
onResize: () => {
if (!containerRef.current || !textRef.current) return;

const containerWidth = containerRef.current.offsetWidth;
const textWidth = textRef.current.offsetWidth;
if (textWidth === 0) return;

const newScale = Math.min(containerWidth / textWidth);
setScale(newScale);
}
});

return (
<FitContainer ref={containerRef}>
<ScalableText {...props} ref={textRef} scale={scale} />
</FitContainer>
);
};

export {
Text,
Heading,
Expand All @@ -121,5 +177,6 @@ export {
UnorderedList,
ListItem,
Link,
CodeSpan
CodeSpan,
FitText
};
3 changes: 2 additions & 1 deletion packages/spectacle/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export {
UnorderedList,
Text,
Link,
CodeSpan
CodeSpan,
FitText
} from './components/typography';
export {
Table,
Expand Down

0 comments on commit 07667fc

Please sign in to comment.