create-application-template 1.3.2 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.husky/pre-commit CHANGED
@@ -1,5 +1,5 @@
1
- npm run lint:fix
2
- npm run stylelint:fix
1
+ npm run lint
2
+ npm run stylelint
3
3
  # use below if you wish to use .css (see README.md "styles")
4
- # npm run stylelint:css:fix
4
+ # npm run stylelint:css
5
5
  npm run test
package/.stylelintrc.js CHANGED
@@ -1,11 +1,14 @@
1
+ // NOTE if you wish to use .css (see README.md "styles")
2
+ // - remove customSyntax and use "npm stylelint:css"
3
+ // - remove 'value-keyword-case', particularly 'ignoreKeywords'
4
+
1
5
  module.exports = {
2
6
  extends: [
3
7
  'stylelint-config-standard',
4
8
  'stylelint-config-recess-order',
5
9
  'stylelint-no-unsupported-browser-features',
6
10
  ],
7
- // NOTE remove customSyntax and use "npm stylelint:css"
8
- // if you wish to use .css (see README.md "styles")
11
+
9
12
  customSyntax: 'postcss-styled-components',
10
13
  ignoreFiles: [],
11
14
  rules: {
@@ -13,5 +16,14 @@ module.exports = {
13
16
  'keyframes-name-pattern': null,
14
17
  'comment-empty-line-before': 'never',
15
18
  'declaration-empty-line-before': 'never',
19
+ 'media-feature-range-notation': 'prefix',
20
+ 'value-keyword-case': [
21
+ 'lower',
22
+ {
23
+ 'ignoreKeywords': [
24
+ '/^POSTCSS_styled-components_\\d+$/',
25
+ ],
26
+ },
27
+ ],
16
28
  },
17
29
  }
@@ -1,10 +1,24 @@
1
- import { render, screen } from '@testing-library/react'
1
+ import { render, screen, act } from '@testing-library/react'
2
2
  import 'jest-styled-components'
3
3
  import { App } from './App'
4
4
 
5
+ jest.useFakeTimers()
6
+
5
7
  test('renders title', () => {
6
8
  render(<App />)
7
9
 
10
+ const text = 'Create Containerized App Template'
11
+
12
+ for (let i = 1; i <= text.length; i++) {
13
+ act(() => {
14
+ jest.advanceTimersByTime(50)
15
+ })
16
+ }
17
+
8
18
  expect(screen).toMatchSnapshot()
9
- expect(screen.getByText(/create containerized app template/i)).toBeInTheDocument()
19
+ expect(screen.getByText(text)).toBeInTheDocument()
20
+ })
21
+
22
+ afterEach(() => {
23
+ jest.useRealTimers()
10
24
  })
@@ -7,6 +7,7 @@ import { StyledLogo } from '../styles/Logo.styled'
7
7
  import '../styles/env.css'
8
8
  import logo from '../assets/logo.svg'
9
9
  import { Counter } from './Counter'
10
+ import { Typewriter } from './Typewriter'
10
11
 
11
12
  export const App: FC = () => {
12
13
  const TemplateLink: FC = () => {
@@ -26,9 +27,28 @@ export const App: FC = () => {
26
27
  <GlobalStyles />
27
28
  <app.StyledContainer>
28
29
  <app.StyledHeader>
29
- <h1>Create Containerized App Template</h1>
30
- <h2>An app template containerized via Docker!</h2>
31
- <h2>Access the CCrATe template <TemplateLink />...</h2>
30
+ <h1>
31
+ <Typewriter
32
+ text={'Create Containerized App Template'}
33
+ speed={50}
34
+ />
35
+ </h1>
36
+ <h2>
37
+ <Typewriter
38
+ text={'An app template containerized via Docker!'}
39
+ speed={50}
40
+ delay={300}
41
+ />
42
+ </h2>
43
+ <h2>
44
+ <Typewriter
45
+ text={'Access the CCrATe template'}
46
+ speed={50}
47
+ delay={1300}
48
+ >
49
+ <TemplateLink />
50
+ </Typewriter>
51
+ </h2>
32
52
  </app.StyledHeader>
33
53
  <app.StyledSection>
34
54
  <code className='card--env'>[NODE_ENV={process.env.NODE_ENV}]</code>
@@ -1,6 +1,6 @@
1
1
  import { styled, keyframes } from 'styled-components'
2
2
 
3
- const logoAnimation = keyframes`
3
+ const pulse = keyframes`
4
4
  from {
5
5
  transform: rotate(15deg);
6
6
  }
@@ -16,6 +16,6 @@ export const StyledLogo = styled.img`
16
16
  transform-origin: top;
17
17
 
18
18
  @media (prefers-reduced-motion: no-preference) {
19
- animation: ${logoAnimation} 3.5s ease-in-out infinite alternate-reverse;
19
+ animation: ${pulse} 3.5s ease-in-out infinite alternate-reverse;
20
20
  }
21
21
  `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-application-template",
3
- "version": "1.3.2",
3
+ "version": "1.5.0",
4
4
  "description": "provides a configured application template for you to build upon",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -75,6 +75,7 @@
75
75
  "@typescript-eslint/parser": "6.5.0",
76
76
  "babel-jest": "29.7.0",
77
77
  "babel-loader": "9.1.3",
78
+ "babel-plugin-styled-components": "2.1.4",
78
79
  "babel-plugin-transform-require-context": "0.1.1",
79
80
  "case-sensitive-paths-webpack-plugin": "2.4.0",
80
81
  "confusing-browser-globals": "1.0.11",
@@ -1,10 +1,24 @@
1
- import { render, screen } from '@testing-library/react'
1
+ import { render, screen, act } from '@testing-library/react'
2
2
  import 'jest-styled-components'
3
3
  import { App } from './App'
4
4
 
5
+ jest.useFakeTimers()
6
+
5
7
  test('renders title', () => {
6
8
  render(<App />)
7
9
 
10
+ const text = 'Create Application Template'
11
+
12
+ for (let i = 1; i <= text.length; i++) {
13
+ act(() => {
14
+ jest.advanceTimersByTime(50)
15
+ })
16
+ }
17
+
8
18
  expect(screen).toMatchSnapshot()
9
- expect(screen.getByText(/create application template/i)).toBeInTheDocument()
19
+ expect(screen.getByText(text)).toBeInTheDocument()
20
+ })
21
+
22
+ afterEach(() => {
23
+ jest.useRealTimers()
10
24
  })
@@ -7,6 +7,7 @@ import { StyledLogo } from '../styles/Logo.styled'
7
7
  import '../styles/env.css'
8
8
  import logo from '../assets/logo.svg'
9
9
  import { Counter } from './Counter'
10
+ import { Typewriter } from './Typewriter'
10
11
 
11
12
  export const App: FC = () => {
12
13
  const TemplateLink: FC = () => {
@@ -26,9 +27,28 @@ export const App: FC = () => {
26
27
  <GlobalStyles />
27
28
  <app.StyledContainer>
28
29
  <app.StyledHeader>
29
- <h1>Create Application Template</h1>
30
- <h2>Configured and under your control!</h2>
31
- <h2>Access the template <TemplateLink />...</h2>
30
+ <h1>
31
+ <Typewriter
32
+ text={'Create Application Template'}
33
+ speed={50}
34
+ />
35
+ </h1>
36
+ <h2>
37
+ <Typewriter
38
+ text={'Configured and under your control!'}
39
+ speed={50}
40
+ delay={300}
41
+ />
42
+ </h2>
43
+ <h2>
44
+ <Typewriter
45
+ text={'Access the template'}
46
+ speed={50}
47
+ delay={1300}
48
+ >
49
+ <TemplateLink />
50
+ </Typewriter>
51
+ </h2>
32
52
  </app.StyledHeader>
33
53
  <app.StyledSection>
34
54
  <code className='card--env'>[NODE_ENV={process.env.NODE_ENV}]</code>
@@ -0,0 +1,36 @@
1
+ import { render, screen, act } from '@testing-library/react'
2
+ import 'jest-styled-components'
3
+ import { Typewriter } from './Typewriter'
4
+
5
+ jest.useFakeTimers()
6
+
7
+ test('renders text and children', () => {
8
+ const text = 'testing'
9
+
10
+ render(
11
+ <Typewriter
12
+ text={text}
13
+ speed={50}
14
+ >
15
+ <div data-testid='child-element'>child</div>
16
+ </Typewriter>
17
+ )
18
+
19
+ for (let i = 1; i <= text.length; i++) {
20
+ // full text available only after all
21
+ // timeouts complete, see Typewriter.tsx
22
+ act(() => {
23
+ jest.advanceTimersByTime(50)
24
+ })
25
+ }
26
+
27
+ expect(screen).toMatchSnapshot()
28
+ expect(screen.getByText(text)).toBeInTheDocument()
29
+ expect(screen.getByTestId('child-element')).toBeInTheDocument()
30
+ })
31
+
32
+ afterEach(() => {
33
+ // reset real timers so other tests
34
+ // are not affected by mock timers
35
+ jest.useRealTimers()
36
+ })
@@ -0,0 +1,52 @@
1
+ import {
2
+ useState,
3
+ useEffect,
4
+ FC,
5
+ ReactElement,
6
+ PropsWithChildren,
7
+ } from 'react'
8
+
9
+ export const Typewriter: FC<
10
+ PropsWithChildren<{
11
+ text:string,
12
+ speed:number,
13
+ delay?:number,
14
+ children?:ReactElement,
15
+ }>
16
+ > = ({
17
+ text,
18
+ speed,
19
+ delay = 0,
20
+ children,
21
+ }) => {
22
+ const [displayedText, setDisplayedText] = useState('')
23
+ const [currentIndex, setCurrentIndex] = useState(0)
24
+
25
+ useEffect(() => {
26
+ const mySpeed = (currentIndex === 0) ? delay + speed : speed
27
+
28
+ if (currentIndex < text.length) {
29
+ const timeoutId = setTimeout(() => {
30
+ setDisplayedText((prevText) => prevText + text.charAt(currentIndex))
31
+ setCurrentIndex((prevIndex) => prevIndex + 1)
32
+ }, mySpeed)
33
+
34
+ return () => clearTimeout(timeoutId)
35
+ }
36
+ }, [
37
+ currentIndex,
38
+ text,
39
+ speed,
40
+ delay,
41
+ ])
42
+
43
+ return (
44
+ <>
45
+ {displayedText}
46
+ {
47
+ (currentIndex === text.length && children) &&
48
+ <>&nbsp;{children}</>
49
+ }
50
+ </>
51
+ )
52
+ }
@@ -0,0 +1,56 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`renders text and children 1`] = `
4
+ {
5
+ "debug": [Function],
6
+ "findAllByAltText": [Function],
7
+ "findAllByDisplayValue": [Function],
8
+ "findAllByLabelText": [Function],
9
+ "findAllByPlaceholderText": [Function],
10
+ "findAllByRole": [Function],
11
+ "findAllByTestId": [Function],
12
+ "findAllByText": [Function],
13
+ "findAllByTitle": [Function],
14
+ "findByAltText": [Function],
15
+ "findByDisplayValue": [Function],
16
+ "findByLabelText": [Function],
17
+ "findByPlaceholderText": [Function],
18
+ "findByRole": [Function],
19
+ "findByTestId": [Function],
20
+ "findByText": [Function],
21
+ "findByTitle": [Function],
22
+ "getAllByAltText": [Function],
23
+ "getAllByDisplayValue": [Function],
24
+ "getAllByLabelText": [Function],
25
+ "getAllByPlaceholderText": [Function],
26
+ "getAllByRole": [Function],
27
+ "getAllByTestId": [Function],
28
+ "getAllByText": [Function],
29
+ "getAllByTitle": [Function],
30
+ "getByAltText": [Function],
31
+ "getByDisplayValue": [Function],
32
+ "getByLabelText": [Function],
33
+ "getByPlaceholderText": [Function],
34
+ "getByRole": [Function],
35
+ "getByTestId": [Function],
36
+ "getByText": [Function],
37
+ "getByTitle": [Function],
38
+ "logTestingPlaygroundURL": [Function],
39
+ "queryAllByAltText": [Function],
40
+ "queryAllByDisplayValue": [Function],
41
+ "queryAllByLabelText": [Function],
42
+ "queryAllByPlaceholderText": [Function],
43
+ "queryAllByRole": [Function],
44
+ "queryAllByTestId": [Function],
45
+ "queryAllByText": [Function],
46
+ "queryAllByTitle": [Function],
47
+ "queryByAltText": [Function],
48
+ "queryByDisplayValue": [Function],
49
+ "queryByLabelText": [Function],
50
+ "queryByPlaceholderText": [Function],
51
+ "queryByRole": [Function],
52
+ "queryByTestId": [Function],
53
+ "queryByText": [Function],
54
+ "queryByTitle": [Function],
55
+ }
56
+ `;
@@ -1,15 +1,25 @@
1
- import { styled } from 'styled-components'
1
+ import { styled, keyframes } from 'styled-components'
2
2
 
3
- export const StyledContainer = styled.div(({ theme }) => `
3
+ const fadeIn = keyframes`
4
+ 0% {
5
+ opacity: 0;
6
+ }
7
+
8
+ 100% {
9
+ opacity: 1;
10
+ }
11
+ `
12
+
13
+ export const StyledContainer = styled.div`
4
14
  display: flex;
5
15
  flex-direction: column;
6
16
  align-items: center;
7
17
  justify-content: center;
8
18
  min-height: 100vh;
9
19
  font-size: max(1em, 18px);
10
- color: ${theme.colors.palette.primary};
11
- background-color: ${theme.colors.palette.background};
12
- `)
20
+ color: ${({ theme }) => theme.colors.palette.primary};
21
+ background-color: ${({ theme }) => theme.colors.palette.background};
22
+ `
13
23
 
14
24
  export const StyledHeader = styled.header`
15
25
  text-align: center;
@@ -19,13 +29,14 @@ export const StyledSection = styled.section`
19
29
  display: flex;
20
30
  flex-direction: column;
21
31
  margin: 2vh;
32
+ animation: 3s ${fadeIn} ease;
22
33
  `
23
34
 
24
- export const StyledLink = styled.a(({ theme }) => `
25
- color: ${theme.colors.link.regular};
35
+ export const StyledLink = styled.a`
36
+ color: ${({ theme }) => theme.colors.link.regular};
26
37
  transition: 0.3s;
27
38
 
28
39
  &:hover {
29
- color: ${theme.colors.link.hover};
40
+ color: ${({ theme }) => theme.colors.link.hover};
30
41
  }
31
- `)
42
+ `
@@ -1,16 +1,16 @@
1
1
  import { styled } from 'styled-components'
2
2
 
3
- export const StyledCounter = styled.button(({ theme }) => `
3
+ export const StyledCounter = styled.button`
4
4
  height: 40px;
5
5
  padding: 10px;
6
6
  font-size: 18px;
7
- color: ${theme.colors.palette.primary};
8
- background-color: ${theme.colors.palette.background};
9
- border: 1px solid ${theme.colors.palette.primary};
7
+ color: ${({ theme }) => theme.colors.palette.primary};
8
+ background-color: ${({ theme }) => theme.colors.palette.background};
9
+ border: 1px solid ${({ theme }) => theme.colors.palette.primary};
10
10
  border-radius: 8px;
11
11
  transition-duration: 0.5s;
12
12
 
13
13
  &:hover {
14
14
  border-radius: 20px;
15
15
  }
16
- `)
16
+ `
@@ -1,6 +1,6 @@
1
1
  import { styled, keyframes } from 'styled-components'
2
2
 
3
- const logoAnimation = keyframes`
3
+ const pulse = keyframes`
4
4
  0% {
5
5
  opacity: 0.2;
6
6
  transform: scale(0.8);
@@ -20,8 +20,5 @@ const logoAnimation = keyframes`
20
20
  export const StyledLogo = styled.img`
21
21
  height: 240px;
22
22
  pointer-events: none;
23
-
24
- @media (prefers-reduced-motion: no-preference) {
25
- animation: ${logoAnimation} infinite 10s ease;
26
- }
23
+ animation: ${pulse} infinite 10s ease;
27
24
  `