@oslokommune/punkt-react 13.6.15 → 13.7.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/CHANGELOG.md +41 -0
- package/dist/index.d.ts +38 -74
- package/dist/punkt-react.es.js +5358 -35971
- package/dist/punkt-react.umd.js +418 -548
- package/package.json +11 -15
- package/src/components/accordion/Accordion.test.tsx +1 -1
- package/src/components/accordion/Accordion.tsx +1 -1
- package/src/components/accordion/AccordionItem.tsx +6 -5
- package/src/components/alert/Alert.tsx +2 -1
- package/src/components/breadcrumbs/Breadcrumbs.test.tsx +16 -4
- package/src/components/breadcrumbs/Breadcrumbs.tsx +3 -2
- package/src/components/button/Button.tsx +3 -2
- package/src/components/checkbox/Checkbox.tsx +4 -3
- package/src/components/datepicker/Datepicker.test.tsx +393 -0
- package/src/components/footer/Footer.tsx +6 -5
- package/src/components/footerSimple/FooterSimple.tsx +4 -3
- package/src/components/icon/Icon.test.tsx +6 -19
- package/src/components/index.ts +0 -2
- package/src/components/input/Input.tsx +4 -3
- package/src/components/radio/RadioButton.tsx +3 -2
- package/src/components/searchinput/SearchInput.tsx +3 -3
- package/src/components/stepper/Stepper.tsx +6 -6
- package/src/components/table/Table.tsx +2 -1
- package/src/components/table/TableBody.tsx +2 -1
- package/src/components/table/TableData.tsx +2 -1
- package/src/components/table/TableDataCell.tsx +2 -1
- package/src/components/table/TableHeader.tsx +2 -1
- package/src/components/table/TableHeaderCell.tsx +2 -1
- package/src/components/table/TableRow.tsx +2 -1
- package/src/components/tag/Tag.tsx +2 -1
- package/src/components/preview/Preview.tsx +0 -274
- package/src/components/preview/PreviewCode.tsx +0 -118
- package/src/components/preview/PreviewPropEditor.tsx +0 -266
- package/src/components/preview/PreviewSpecs.tsx +0 -125
- package/src/components/preview/PreviewWithJson.tsx +0 -519
- package/src/components/preview/preview-types.ts +0 -42
- package/src/components/preview/preview-utils.ts +0 -226
- package/src/components/preview/previewJson/accordion.json +0 -84
- package/src/components/preview/previewJson/alert.json +0 -27
- package/src/components/preview/previewJson/backlink.json +0 -14
- package/src/components/preview/previewJson/breadcrumbs.json +0 -17
- package/src/components/preview/previewJson/button.json +0 -28
- package/src/components/preview/previewJson/card.json +0 -41
- package/src/components/preview/previewJson/checkbox.json +0 -21
- package/src/components/preview/previewJson/combobox.json +0 -24
- package/src/components/preview/previewJson/consent.json +0 -14
- package/src/components/preview/previewJson/datepicker.json +0 -14
- package/src/components/preview/previewJson/footer-simple.json +0 -17
- package/src/components/preview/previewJson/footer.json +0 -29
- package/src/components/preview/previewJson/header.json +0 -34
- package/src/components/preview/previewJson/icon.json +0 -13
- package/src/components/preview/previewJson/input-wrapper.json +0 -39
- package/src/components/preview/previewJson/link.json +0 -28
- package/src/components/preview/previewJson/linkcard.json +0 -30
- package/src/components/preview/previewJson/loader.json +0 -28
- package/src/components/preview/previewJson/messagebox.json +0 -28
- package/src/components/preview/previewJson/modal.json +0 -65
- package/src/components/preview/previewJson/progressbar.json +0 -16
- package/src/components/preview/previewJson/radiobutton.json +0 -21
- package/src/components/preview/previewJson/searchinput.json +0 -20
- package/src/components/preview/previewJson/select.json +0 -73
- package/src/components/preview/previewJson/steps.json +0 -72
- package/src/components/preview/previewJson/table.json +0 -130
- package/src/components/preview/previewJson/tabs.json +0 -33
- package/src/components/preview/previewJson/tag.json +0 -26
- package/src/components/preview/previewJson/textarea.json +0 -18
- package/src/components/preview/previewJson/textinput.json +0 -18
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { FC } from 'react'
|
|
4
|
+
import type { HTMLAttributes } from 'react'
|
|
4
5
|
|
|
5
6
|
import { PktIcon } from '../icon/Icon'
|
|
6
7
|
import { PktConsent } from '../consent/Consent'
|
|
7
8
|
|
|
8
|
-
export interface IPktFooterSimple extends
|
|
9
|
+
export interface IPktFooterSimple extends HTMLAttributes<HTMLDivElement> {
|
|
9
10
|
links?: Array<{
|
|
10
11
|
href: string
|
|
11
12
|
text: string
|
|
@@ -25,7 +26,7 @@ export interface IPktFooterSimple extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
25
26
|
onToggleConsent?: (e: CustomEvent) => void
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
export const PktFooterSimple:
|
|
29
|
+
export const PktFooterSimple: FC<IPktFooterSimple> = ({
|
|
29
30
|
links = [],
|
|
30
31
|
openLinksInNewTab = false,
|
|
31
32
|
personvernOgInfoLink = 'https://www.oslo.kommune.no/personvern-og-informasjonskapsler/',
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import '@testing-library/jest-dom'
|
|
2
2
|
|
|
3
3
|
import { render, screen, waitFor } from '../../utils/test-utils'
|
|
4
|
-
import React from 'react'
|
|
5
4
|
|
|
6
5
|
import { PktIcon } from './Icon'
|
|
7
6
|
|
|
@@ -11,26 +10,14 @@ declare global {
|
|
|
11
10
|
pktIconPath: string
|
|
12
11
|
}
|
|
13
12
|
}
|
|
14
|
-
window.pktFetch = (file = 'filnavn.svg') =>
|
|
15
|
-
Promise.resolve({
|
|
16
|
-
ok: true,
|
|
17
|
-
text: () => Promise.resolve(`<svg role="img" data-testid="icon" data-filename=${file}>Her er SVGen</svg>`),
|
|
18
|
-
})
|
|
19
|
-
window.pktIconPath = 'sti'
|
|
20
13
|
|
|
21
14
|
describe('PktIcon', () => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
setItem: function (key: string, value: string) {
|
|
29
|
-
store[key] = value.toString()
|
|
30
|
-
},
|
|
31
|
-
}
|
|
32
|
-
})()
|
|
33
|
-
Object.defineProperty(global, 'localStorage', { value: localStorageMock })
|
|
15
|
+
window.pktFetch = (file = 'filnavn.svg') =>
|
|
16
|
+
Promise.resolve({
|
|
17
|
+
ok: true,
|
|
18
|
+
text: () => Promise.resolve(`<svg role="img" data-testid="icon" data-filename=${file}>Her er SVGen</svg>`),
|
|
19
|
+
})
|
|
20
|
+
window.pktIconPath = 'sti'
|
|
34
21
|
|
|
35
22
|
describe('with default fetcher', () => {
|
|
36
23
|
test('fetches SVG with default fetcher', async () => {
|
package/src/components/index.ts
CHANGED
|
@@ -22,8 +22,6 @@ export { PktLinkCard } from './linkcard/LinkCard'
|
|
|
22
22
|
export { PktLoader } from './loader/Loader'
|
|
23
23
|
export { PktMessagebox } from './messagebox/Messagebox'
|
|
24
24
|
export { PktModal } from './modal/Modal'
|
|
25
|
-
export { PktPreview } from './preview/Preview'
|
|
26
|
-
export { PktPreviewWithJson } from './preview/PreviewWithJson'
|
|
27
25
|
export { PktProgressbar } from './progressbar/Progressbar'
|
|
28
26
|
export { PktRadioButton } from './radio/RadioButton'
|
|
29
27
|
export { PktSearchInput } from './searchinput/SearchInput'
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { ForwardedRef, forwardRef } from 'react'
|
|
4
|
+
import type { InputHTMLAttributes } from 'react'
|
|
4
5
|
|
|
5
|
-
interface InputProps extends
|
|
6
|
+
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
6
7
|
/** The input's label */
|
|
7
8
|
label?: string
|
|
8
9
|
/** The input's id */
|
|
@@ -21,7 +22,7 @@ interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
|
21
22
|
* <Input label="First name" id="firstName" validationMessage="First name is required" />
|
|
22
23
|
*
|
|
23
24
|
*/
|
|
24
|
-
export const PktInput =
|
|
25
|
+
export const PktInput = forwardRef(
|
|
25
26
|
({ label, id, children, ...props }: InputProps, ref: ForwardedRef<HTMLInputElement>): React.ReactElement => {
|
|
26
27
|
return (
|
|
27
28
|
<div className="pkt-form-group">
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ChangeEventHandler, ForwardedRef, forwardRef, ReactNode } from 'react'
|
|
2
|
+
import type { InputHTMLAttributes } from 'react'
|
|
2
3
|
|
|
3
|
-
export interface IPktRadioButton extends
|
|
4
|
+
export interface IPktRadioButton extends InputHTMLAttributes<HTMLInputElement> {
|
|
4
5
|
id: string
|
|
5
6
|
name: string
|
|
6
7
|
label: string
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { ChangeEvent, forwardRef, HTMLProps, ReactNode, createElement } from 'react'
|
|
4
4
|
|
|
5
5
|
import { PktButton } from '../button/Button'
|
|
6
6
|
|
|
@@ -33,7 +33,7 @@ interface ISearchForm extends ISearch {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
interface ISearchInput extends ISearch {
|
|
36
|
-
onChange: (event:
|
|
36
|
+
onChange: (event: ChangeEvent<HTMLInputElement>) => void
|
|
37
37
|
disabled?: boolean
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -145,7 +145,7 @@ export const PktSearchInput = forwardRef<HTMLInputElement, ISearchInput | ISearc
|
|
|
145
145
|
<ul id={`${id}-suggestions`} className="pkt-searchinput__suggestions" aria-live="assertive">
|
|
146
146
|
{suggestions.map((suggestion, index) => (
|
|
147
147
|
<li key={`search-suggestion-${index}`}>
|
|
148
|
-
{
|
|
148
|
+
{createElement(
|
|
149
149
|
suggestion.href ? 'a' : suggestion.onClick ? 'button' : 'div',
|
|
150
150
|
{
|
|
151
151
|
href: suggestion.href,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { ReactElement, Ref, forwardRef } from 'react'
|
|
3
|
+
import { ReactElement, Ref, forwardRef, Children, isValidElement, cloneElement } from 'react'
|
|
4
|
+
import type { HTMLAttributes } from 'react'
|
|
4
5
|
import { IPktStep } from './Step'
|
|
5
6
|
import classNames from 'classnames'
|
|
6
|
-
import React from 'react'
|
|
7
7
|
|
|
8
|
-
export interface IPktStepper extends
|
|
8
|
+
export interface IPktStepper extends HTMLAttributes<HTMLOListElement> {
|
|
9
9
|
/**
|
|
10
10
|
* The index of the current active step
|
|
11
11
|
*/
|
|
@@ -55,9 +55,9 @@ export const PktStepper = forwardRef(
|
|
|
55
55
|
orientation === 'horizontal' ? 'pkt-stepper--horizontal' : 'pkt-stepper--vertical',
|
|
56
56
|
)
|
|
57
57
|
|
|
58
|
-
const childrenWithProps =
|
|
59
|
-
if (
|
|
60
|
-
return
|
|
58
|
+
const childrenWithProps = Children.map(children, (child, index) => {
|
|
59
|
+
if (isValidElement(child)) {
|
|
60
|
+
return cloneElement(child, {
|
|
61
61
|
className: classNames(child.props.className, {
|
|
62
62
|
'pkt-step--hideStep': hideNonActiveSteps && index !== activeStep,
|
|
63
63
|
'pkt-step--hideContent': hideNonActiveStepsContent && index !== activeStep,
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames'
|
|
4
4
|
import * as React from 'react'
|
|
5
|
+
import type { HTMLAttributes } from 'react'
|
|
5
6
|
|
|
6
7
|
export type TTableSkin = 'basic' | 'zebra-blue'
|
|
7
8
|
|
|
8
|
-
interface ITableProps extends
|
|
9
|
+
interface ITableProps extends HTMLAttributes<HTMLTableElement> {
|
|
9
10
|
compact?: boolean
|
|
10
11
|
skin?: 'basic' | 'zebra-blue'
|
|
11
12
|
responsiveView?: boolean
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames'
|
|
4
4
|
import * as React from 'react'
|
|
5
|
+
import type { HTMLAttributes } from 'react'
|
|
5
6
|
|
|
6
|
-
interface ITableBodyProps extends
|
|
7
|
+
interface ITableBodyProps extends HTMLAttributes<HTMLTableSectionElement> {
|
|
7
8
|
className?: string
|
|
8
9
|
children: React.ReactNode
|
|
9
10
|
}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames'
|
|
4
4
|
import * as React from 'react'
|
|
5
|
+
import type { HTMLAttributes } from 'react'
|
|
5
6
|
|
|
6
|
-
interface TableDataProps extends
|
|
7
|
+
interface TableDataProps extends HTMLAttributes<HTMLTableCellElement> {
|
|
7
8
|
className?: string
|
|
8
9
|
children: React.ReactNode | React.ReactNode[]
|
|
9
10
|
}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames'
|
|
4
4
|
import * as React from 'react'
|
|
5
|
+
import type { HTMLAttributes } from 'react'
|
|
5
6
|
|
|
6
|
-
interface ITableDataCellProps extends
|
|
7
|
+
interface ITableDataCellProps extends HTMLAttributes<HTMLTableCellElement> {
|
|
7
8
|
className?: string
|
|
8
9
|
children?: React.ReactNode
|
|
9
10
|
dataLabel?: string
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames'
|
|
4
4
|
import * as React from 'react'
|
|
5
|
+
import type { HTMLAttributes } from 'react'
|
|
5
6
|
|
|
6
|
-
interface ITableHeaderProps extends
|
|
7
|
+
interface ITableHeaderProps extends HTMLAttributes<HTMLTableSectionElement> {
|
|
7
8
|
className?: string
|
|
8
9
|
children: React.ReactNode
|
|
9
10
|
}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames'
|
|
4
4
|
import * as React from 'react'
|
|
5
|
+
import type { HTMLAttributes } from 'react'
|
|
5
6
|
|
|
6
|
-
interface ITableHeaderCellProps extends
|
|
7
|
+
interface ITableHeaderCellProps extends HTMLAttributes<HTMLTableCellElement> {
|
|
7
8
|
className?: string
|
|
8
9
|
children: React.ReactNode
|
|
9
10
|
}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames'
|
|
4
4
|
import * as React from 'react'
|
|
5
|
+
import type { HTMLAttributes } from 'react'
|
|
5
6
|
|
|
6
|
-
interface ITableRowProps extends
|
|
7
|
+
interface ITableRowProps extends HTMLAttributes<HTMLTableRowElement> {
|
|
7
8
|
className?: string
|
|
8
9
|
children: React.ReactNode
|
|
9
10
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { FC, ForwardedRef, forwardRef, ReactElement } from 'react'
|
|
4
|
+
import * as React from 'react'
|
|
4
5
|
import { createComponent, EventName } from '@lit/react'
|
|
5
6
|
import { PktTag as PktElTag } from '@oslokommune/punkt-elements'
|
|
6
7
|
import type { PktElType, PktElConstructor } from '@/interfaces/IPktElements'
|
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import React, { cloneElement, isValidElement, ReactNode, useRef, useState } from 'react'
|
|
4
|
-
import reactElementToJSXString from 'react-element-to-jsx-string'
|
|
5
|
-
import * as prettier from 'prettier/standalone'
|
|
6
|
-
import * as parserHtml from 'prettier/parser-html'
|
|
7
|
-
import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter'
|
|
8
|
-
import jsx from 'react-syntax-highlighter/dist/esm/languages/prism/jsx'
|
|
9
|
-
import { prism } from 'react-syntax-highlighter/dist/esm/styles/prism'
|
|
10
|
-
import { PktButton } from '../button/Button'
|
|
11
|
-
import { PktCheckbox } from '../checkbox/Checkbox'
|
|
12
|
-
import { PktTabs } from '../tabs/Tabs'
|
|
13
|
-
import { PktTag } from '../tag/Tag'
|
|
14
|
-
import { PktTextinput } from '../textinput/Textinput'
|
|
15
|
-
import { PktPreviewSpecs } from './PreviewSpecs'
|
|
16
|
-
import type { ISpecObject } from './preview-types'
|
|
17
|
-
import { PktPreviewPropEditor } from './PreviewPropEditor'
|
|
18
|
-
|
|
19
|
-
SyntaxHighlighter.registerLanguage('jsx', jsx)
|
|
20
|
-
|
|
21
|
-
interface PreviewProps {
|
|
22
|
-
children?: ReactNode
|
|
23
|
-
specs: ISpecObject
|
|
24
|
-
fullWidth?: boolean
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export const PktPreview: React.FC<PreviewProps> = ({ specs, children, fullWidth }) => {
|
|
28
|
-
const initialProps =
|
|
29
|
-
specs.props && typeof specs.props === 'object' && !Array.isArray(specs.props)
|
|
30
|
-
? Object.entries(specs.props).reduce<Record<string, any>>((acc, [key, value]) => {
|
|
31
|
-
if (
|
|
32
|
-
typeof value === 'object' &&
|
|
33
|
-
!Array.isArray(value) &&
|
|
34
|
-
value.previewDefault !== undefined &&
|
|
35
|
-
value.previewDefault !== null
|
|
36
|
-
) {
|
|
37
|
-
if (value.previewDefault === 'false') {
|
|
38
|
-
value.previewDefault = false
|
|
39
|
-
}
|
|
40
|
-
if (value.previewDefault === 'true') {
|
|
41
|
-
value.previewDefault = true
|
|
42
|
-
}
|
|
43
|
-
acc[key] = value.previewDefault
|
|
44
|
-
} else if (
|
|
45
|
-
typeof value === 'object' &&
|
|
46
|
-
!Array.isArray(value) &&
|
|
47
|
-
value.default !== undefined &&
|
|
48
|
-
value.default !== null
|
|
49
|
-
) {
|
|
50
|
-
if (value.default === 'false') {
|
|
51
|
-
value.default = false
|
|
52
|
-
}
|
|
53
|
-
if (value.default === 'true') {
|
|
54
|
-
value.default = true
|
|
55
|
-
}
|
|
56
|
-
acc[key] = value.default
|
|
57
|
-
}
|
|
58
|
-
return acc
|
|
59
|
-
}, {})
|
|
60
|
-
: {}
|
|
61
|
-
const [iteratedKey, setIteratedKey] = useState(0)
|
|
62
|
-
const [props, setProps] = useState(initialProps)
|
|
63
|
-
const [mode, setMode] = useState<'light' | 'dark'>('light')
|
|
64
|
-
const [htmlContent, setHtmlContent] = useState('')
|
|
65
|
-
const [jsxContent, setJsxContent] = useState('')
|
|
66
|
-
const [copied, setCopied] = useState('')
|
|
67
|
-
const [slotContent, setSlotContent] = useState('Innhold')
|
|
68
|
-
|
|
69
|
-
const [tabs, setTabs] = useState([
|
|
70
|
-
{
|
|
71
|
-
text: 'Rediger',
|
|
72
|
-
icon: 'adjust',
|
|
73
|
-
active: true,
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
text: 'Kode (React)',
|
|
77
|
-
icon: 'react',
|
|
78
|
-
active: false,
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
text: `Kode (${specs.isElement ? 'Element' : 'HTML'})`,
|
|
82
|
-
icon: 'code',
|
|
83
|
-
active: false,
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
text: 'Egenskaper',
|
|
87
|
-
icon: 'list',
|
|
88
|
-
active: false,
|
|
89
|
-
},
|
|
90
|
-
])
|
|
91
|
-
|
|
92
|
-
const changeContent = (id: number) => {
|
|
93
|
-
if (id === 1) {
|
|
94
|
-
fetchJsx()
|
|
95
|
-
}
|
|
96
|
-
if (id === 2) {
|
|
97
|
-
fetchHtml()
|
|
98
|
-
}
|
|
99
|
-
setCopied('')
|
|
100
|
-
setTabs((prevTabs) =>
|
|
101
|
-
prevTabs.map((tab, index) => ({
|
|
102
|
-
...tab,
|
|
103
|
-
active: index === id,
|
|
104
|
-
})),
|
|
105
|
-
)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const previewComponent = useRef<any>(null)
|
|
109
|
-
|
|
110
|
-
const fetchHtml = async () => {
|
|
111
|
-
if (specs.isElement && isValidElement(component)) {
|
|
112
|
-
const componentType = isValidElement(component)
|
|
113
|
-
? (component.type as any).displayName || (component.type as any).name
|
|
114
|
-
: specs.name
|
|
115
|
-
|
|
116
|
-
const attributes = Object.entries(props)
|
|
117
|
-
.map(([key, value]) => {
|
|
118
|
-
if (value && !isEmptyArray(value) && !isEmptyObject(value)) {
|
|
119
|
-
return typeof value === 'object' ? `${key}='${JSON.stringify(value)}'` : `${key}="${value}"`
|
|
120
|
-
}
|
|
121
|
-
})
|
|
122
|
-
.join(' ')
|
|
123
|
-
|
|
124
|
-
const customElement = await prettier.format(
|
|
125
|
-
`<${specs.name || componentType} ${attributes}>${slotContent}</${specs.name || componentType}>`,
|
|
126
|
-
{
|
|
127
|
-
parser: 'html',
|
|
128
|
-
plugins: [parserHtml as any],
|
|
129
|
-
},
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
setHtmlContent(
|
|
133
|
-
'<!-- Denne koden bør testes grundig før bruk. Spør en Punkt-utvikler om du er usikker. -->\n' + customElement,
|
|
134
|
-
)
|
|
135
|
-
} else {
|
|
136
|
-
const html = await prettier.format(previewComponent.current.innerHTML, {
|
|
137
|
-
parser: 'html',
|
|
138
|
-
plugins: [parserHtml as any],
|
|
139
|
-
})
|
|
140
|
-
setHtmlContent(html.replace(/<\!--.*?-->/g, ''))
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const fetchJsx = async () => {
|
|
145
|
-
const jsx = await reactElementToJSXString(component)
|
|
146
|
-
setJsxContent(jsx)
|
|
147
|
-
return Promise.resolve()
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const copyToClipboard = (type: string, content: string) => {
|
|
151
|
-
navigator.clipboard.writeText(content).then(() => {
|
|
152
|
-
setCopied(type)
|
|
153
|
-
console.log('Copied to clipboard', type)
|
|
154
|
-
})
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const isEmptyArray = (arr: any) => Array.isArray(arr) && arr.length === 0
|
|
158
|
-
const isEmptyObject = (obj: any) => obj && typeof obj === 'object' && !Object.keys(obj).length
|
|
159
|
-
|
|
160
|
-
const CopyButton = (type: string, content: string) => (
|
|
161
|
-
<div className="pkt-preview__copy">
|
|
162
|
-
{copied === type && <span className="pkt-preview__copied">Kode kopiert til utklippstavle</span>}
|
|
163
|
-
<PktButton
|
|
164
|
-
skin="tertiary"
|
|
165
|
-
variant="icon-only"
|
|
166
|
-
size="small"
|
|
167
|
-
iconName="copy"
|
|
168
|
-
onClick={() => copyToClipboard(type, content)}
|
|
169
|
-
>
|
|
170
|
-
Kopier kode
|
|
171
|
-
</PktButton>
|
|
172
|
-
</div>
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
const component =
|
|
176
|
-
isValidElement(children) &&
|
|
177
|
-
cloneElement(
|
|
178
|
-
children,
|
|
179
|
-
{ ...props },
|
|
180
|
-
children.props.children ? children.props.children : specs.slots?.default ? slotContent : null,
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
const handleChange = (key: string, value: any, displayAsFalse: boolean = false) => {
|
|
184
|
-
setIteratedKey(iteratedKey + 1)
|
|
185
|
-
if (!displayAsFalse && (!value || value == 'false')) {
|
|
186
|
-
const { [key]: _, ...rest } = props
|
|
187
|
-
setProps(rest)
|
|
188
|
-
} else {
|
|
189
|
-
setProps((prevProps) => ({
|
|
190
|
-
...prevProps,
|
|
191
|
-
[key]: value,
|
|
192
|
-
}))
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return (
|
|
197
|
-
<div className="pkt-preview">
|
|
198
|
-
<div className="pkt-preview__component-container" data-mode={mode}>
|
|
199
|
-
{specs['dark-mode'] && (
|
|
200
|
-
<div className="pkt-preview__mode">
|
|
201
|
-
<PktCheckbox
|
|
202
|
-
id="mode"
|
|
203
|
-
label="Dark mode"
|
|
204
|
-
type="checkbox"
|
|
205
|
-
checked={mode === 'dark'}
|
|
206
|
-
onChange={(e) => setMode(e.target.checked ? 'dark' : 'light')}
|
|
207
|
-
labelPosition="right"
|
|
208
|
-
isSwitch
|
|
209
|
-
/>
|
|
210
|
-
</div>
|
|
211
|
-
)}
|
|
212
|
-
<div
|
|
213
|
-
className={`pkt-preview__component ${fullWidth && 'pkt-preview__component--fullwidth'}`}
|
|
214
|
-
ref={previewComponent}
|
|
215
|
-
key={iteratedKey}
|
|
216
|
-
>
|
|
217
|
-
<div>{component}</div>
|
|
218
|
-
</div>
|
|
219
|
-
</div>
|
|
220
|
-
<PktTabs tabs={tabs} onTabSelected={changeContent}></PktTabs>
|
|
221
|
-
<div className={`pkt-grid pkt-preview__opts ${tabs[0].active ? '' : 'pkt-hide'}`}>
|
|
222
|
-
{specs.slots?.default && !(children as React.ReactElement).props.children && (
|
|
223
|
-
<div className="pkt-cell pkt-cell--span12 pkt-cell--span6-phablet-up">
|
|
224
|
-
<PktTag size="small" skin="blue-light" textStyle="thin-text">
|
|
225
|
-
children
|
|
226
|
-
</PktTag>
|
|
227
|
-
<PktTextinput
|
|
228
|
-
id="slot"
|
|
229
|
-
label="Slot"
|
|
230
|
-
helptext={specs.slots.default.description || 'Innholdet i slot'}
|
|
231
|
-
type="text"
|
|
232
|
-
value={slotContent}
|
|
233
|
-
onChange={(e) => setSlotContent(e.currentTarget.value)}
|
|
234
|
-
/>
|
|
235
|
-
</div>
|
|
236
|
-
)}
|
|
237
|
-
{specs.props && typeof specs.props === 'object' && !Array.isArray(specs.props) ? (
|
|
238
|
-
Object.entries(specs.props).map(([key, prop]) => (
|
|
239
|
-
<div className="pkt-cell pkt-cell--span12 pkt-cell--span6-phablet-up" key={key}>
|
|
240
|
-
<PktTag size="small" skin="blue-light" textStyle="thin-text">
|
|
241
|
-
{key}
|
|
242
|
-
</PktTag>
|
|
243
|
-
<PktPreviewPropEditor
|
|
244
|
-
propKey={key}
|
|
245
|
-
props={props}
|
|
246
|
-
spec={prop}
|
|
247
|
-
handleChange={handleChange}
|
|
248
|
-
></PktPreviewPropEditor>
|
|
249
|
-
</div>
|
|
250
|
-
))
|
|
251
|
-
) : (
|
|
252
|
-
<p>OBS! Specs mangler props!</p>
|
|
253
|
-
)}
|
|
254
|
-
</div>
|
|
255
|
-
<div className={`pkt-preview__code ${tabs[1].active ? '' : 'pkt-hide'}`}>
|
|
256
|
-
<SyntaxHighlighter language="jsx" style={prism}>
|
|
257
|
-
{jsxContent}
|
|
258
|
-
</SyntaxHighlighter>
|
|
259
|
-
{CopyButton('jsx', jsxContent)}
|
|
260
|
-
</div>
|
|
261
|
-
<div className={`pkt-preview__code ${tabs[2].active ? '' : 'pkt-hide'}`}>
|
|
262
|
-
<SyntaxHighlighter language="html" style={prism}>
|
|
263
|
-
{htmlContent}
|
|
264
|
-
</SyntaxHighlighter>
|
|
265
|
-
{CopyButton('html', htmlContent)}
|
|
266
|
-
</div>
|
|
267
|
-
<div className={`pkt-preview__specs ${tabs[3].active ? '' : 'pkt-hide'}`}>
|
|
268
|
-
<PktPreviewSpecs specs={specs}></PktPreviewSpecs>
|
|
269
|
-
</div>
|
|
270
|
-
</div>
|
|
271
|
-
)
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
PktPreview.displayName = 'PktPreview'
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react'
|
|
2
|
-
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
|
|
3
|
-
import { a11yDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
|
|
4
|
-
import * as prettier from 'prettier/standalone'
|
|
5
|
-
import * as parserHtml from 'prettier/parser-html'
|
|
6
|
-
import type { TPreviewTreeNode, TSpecs } from './preview-types'
|
|
7
|
-
import { PktButton } from '../button/Button'
|
|
8
|
-
import { PktTabs } from '../tabs/Tabs'
|
|
9
|
-
|
|
10
|
-
import { sanitizeCode, buildElementsCode, buildReactCode } from './preview-utils'
|
|
11
|
-
|
|
12
|
-
export const PktPreviewCode: React.FC<{
|
|
13
|
-
tree: TPreviewTreeNode
|
|
14
|
-
specs: TSpecs
|
|
15
|
-
html: string
|
|
16
|
-
}> = ({ tree, specs, html }) => {
|
|
17
|
-
const [tabs, setTabs] = useState([
|
|
18
|
-
{ text: 'Punkt React', icon: 'react', active: true },
|
|
19
|
-
{ text: specs[tree.spec || ''].isElement ? 'Punkt Elements' : 'HTML', icon: 'code', active: false },
|
|
20
|
-
])
|
|
21
|
-
const [copied, setCopied] = useState<'jsx' | 'html' | ''>('')
|
|
22
|
-
const [activeTab, setActiveTab] = useState(0)
|
|
23
|
-
|
|
24
|
-
const htmlCodeRaw = specs[tree.spec || ''].isElement
|
|
25
|
-
? (buildElementsCode(tree, specs) ?? '').replace(/^\s*\n|\n\s*$/g, '').trim()
|
|
26
|
-
: html.replace(/<\!--.*?-->/g, '')
|
|
27
|
-
const [htmlCode, setHtmlCode] = useState('') // Start as empty string
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
if (htmlCodeRaw) {
|
|
30
|
-
prettier
|
|
31
|
-
.format(htmlCodeRaw, {
|
|
32
|
-
parser: 'html',
|
|
33
|
-
plugins: [parserHtml as any],
|
|
34
|
-
singleAttributePerLine: true,
|
|
35
|
-
bracketSameLine: true,
|
|
36
|
-
})
|
|
37
|
-
.then((formatted) => setHtmlCode(formatted.trim()))
|
|
38
|
-
.catch(() => setHtmlCode(htmlCodeRaw)) // fallback if prettier fails
|
|
39
|
-
} else {
|
|
40
|
-
setHtmlCode('')
|
|
41
|
-
}
|
|
42
|
-
}, [htmlCodeRaw])
|
|
43
|
-
|
|
44
|
-
const jsxCodeRaw = (buildReactCode(tree, specs) ?? '').replace(/^\s*\n|\n\s*$/g, '').trim()
|
|
45
|
-
const [jsxCode, setJsxCode] = useState('') // Start as empty string
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
if (jsxCodeRaw) {
|
|
48
|
-
prettier
|
|
49
|
-
.format(jsxCodeRaw, {
|
|
50
|
-
parser: 'html',
|
|
51
|
-
plugins: [parserHtml as any],
|
|
52
|
-
singleAttributePerLine: true,
|
|
53
|
-
bracketSameLine: true,
|
|
54
|
-
})
|
|
55
|
-
.then((formatted) => setJsxCode(formatted.trim()))
|
|
56
|
-
.catch(() => setJsxCode(jsxCodeRaw)) // fallback if prettier fails
|
|
57
|
-
} else {
|
|
58
|
-
setJsxCode('')
|
|
59
|
-
}
|
|
60
|
-
}, [jsxCodeRaw])
|
|
61
|
-
|
|
62
|
-
const changeTab = (id: number) => {
|
|
63
|
-
setActiveTab(id)
|
|
64
|
-
setCopied('')
|
|
65
|
-
setTabs((prevTabs) =>
|
|
66
|
-
prevTabs.map((tab, index) => ({
|
|
67
|
-
...tab,
|
|
68
|
-
active: index === id,
|
|
69
|
-
})),
|
|
70
|
-
)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const copyToClipboard = (type: 'jsx' | 'html', content: string) => {
|
|
74
|
-
navigator.clipboard.writeText(content).then(() => setCopied(type))
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<div className="pkt-preview__code-container" data-mode="dark">
|
|
79
|
-
<PktTabs tabs={tabs} onTabSelected={changeTab} />
|
|
80
|
-
<div className={`pkt-preview__code${activeTab === 0 ? '' : ' pkt-hide'}`}>
|
|
81
|
-
<SyntaxHighlighter language="jsx" style={a11yDark}>
|
|
82
|
-
{typeof jsxCode === 'string' && jsxCode.length > 0 ? sanitizeCode(jsxCode) : ''}
|
|
83
|
-
</SyntaxHighlighter>
|
|
84
|
-
|
|
85
|
-
<div className="pkt-preview__copy">
|
|
86
|
-
{copied === 'jsx' && <span className="pkt-preview__copied">Kode kopiert til utklippstavle</span>}
|
|
87
|
-
<PktButton
|
|
88
|
-
skin="tertiary"
|
|
89
|
-
variant="icon-only"
|
|
90
|
-
size="small"
|
|
91
|
-
iconName="copy"
|
|
92
|
-
onClick={() => copyToClipboard('jsx', jsxCode)}
|
|
93
|
-
>
|
|
94
|
-
Kopier kode
|
|
95
|
-
</PktButton>
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
<div className={`pkt-preview__code${activeTab === 1 ? '' : ' pkt-hide'}`}>
|
|
99
|
-
<SyntaxHighlighter language="markup" style={a11yDark}>
|
|
100
|
-
{typeof htmlCode === 'string' && htmlCode.length > 0 ? sanitizeCode(htmlCode) : ''}
|
|
101
|
-
</SyntaxHighlighter>
|
|
102
|
-
|
|
103
|
-
<div className="pkt-preview__copy">
|
|
104
|
-
{copied === 'html' && <span className="pkt-preview__copied">Kode kopiert til utklippstavle</span>}
|
|
105
|
-
<PktButton
|
|
106
|
-
skin="tertiary"
|
|
107
|
-
variant="icon-only"
|
|
108
|
-
size="small"
|
|
109
|
-
iconName="copy"
|
|
110
|
-
onClick={() => copyToClipboard('html', htmlCode.trim())}
|
|
111
|
-
>
|
|
112
|
-
Kopier kode
|
|
113
|
-
</PktButton>
|
|
114
|
-
</div>
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
117
|
-
)
|
|
118
|
-
}
|