@elliemae/ds-read-more 3.53.0-alpha.1 → 3.53.0-alpha.3

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.
Files changed (54) hide show
  1. package/dist/cjs/DSReadMore.js +25 -12
  2. package/dist/cjs/DSReadMore.js.map +2 -2
  3. package/dist/cjs/MoreLessButton.js +26 -28
  4. package/dist/cjs/MoreLessButton.js.map +2 -2
  5. package/dist/cjs/{DSReadMoreDefinitions.js → constants/index.js} +13 -8
  6. package/dist/cjs/constants/index.js.map +7 -0
  7. package/dist/cjs/index.js +6 -4
  8. package/dist/cjs/index.js.map +2 -2
  9. package/dist/cjs/react-desc-prop-types.js +3 -2
  10. package/dist/cjs/react-desc-prop-types.js.map +2 -2
  11. package/dist/cjs/styled.js +17 -12
  12. package/dist/cjs/styled.js.map +2 -2
  13. package/dist/cjs/useReadMoreTruncate.js +50 -37
  14. package/dist/cjs/useReadMoreTruncate.js.map +2 -2
  15. package/dist/cjs/utils.js +40 -0
  16. package/dist/cjs/utils.js.map +7 -0
  17. package/dist/esm/DSReadMore.js +23 -10
  18. package/dist/esm/DSReadMore.js.map +2 -2
  19. package/dist/esm/MoreLessButton.js +26 -28
  20. package/dist/esm/MoreLessButton.js.map +2 -2
  21. package/dist/esm/{DSReadMoreDefinitions.js → constants/index.js} +10 -5
  22. package/dist/esm/constants/index.js.map +7 -0
  23. package/dist/esm/index.js +9 -1
  24. package/dist/esm/index.js.map +2 -2
  25. package/dist/esm/react-desc-prop-types.js +3 -1
  26. package/dist/esm/react-desc-prop-types.js.map +2 -2
  27. package/dist/esm/styled.js +15 -10
  28. package/dist/esm/styled.js.map +2 -2
  29. package/dist/esm/useReadMoreTruncate.js +50 -37
  30. package/dist/esm/useReadMoreTruncate.js.map +2 -2
  31. package/dist/esm/utils.js +10 -0
  32. package/dist/esm/utils.js.map +7 -0
  33. package/dist/types/MoreLessButton.d.ts +0 -1
  34. package/dist/types/constants/index.d.ts +33 -0
  35. package/dist/types/index.d.ts +1 -1
  36. package/dist/types/react-desc-prop-types.d.ts +5 -4
  37. package/dist/types/styled.d.ts +8 -5
  38. package/dist/types/tests/DSReadMore.PUI-14965.test.d.ts +1 -0
  39. package/dist/types/tests/DSReadMore.a11y.test.d.ts +1 -0
  40. package/dist/types/tests/DSReadMore.data-testid.test.d.ts +1 -0
  41. package/dist/types/tests/DSReadMore.default-props.test.d.ts +1 -0
  42. package/dist/types/tests/DSReadMore.events.test.d.ts +1 -0
  43. package/dist/types/tests/DSReadMore.exports.test.d.ts +1 -0
  44. package/dist/types/tests/DSReadMore.get-owner-props.test.d.ts +1 -0
  45. package/dist/types/tests/DSReadMore.keyboard.test.d.ts +1 -0
  46. package/dist/types/tests/playwright/DSReadMore.ControlledTestRenderer.d.ts +1 -0
  47. package/dist/types/tests/playwright/DSReadMore.test.playwright.d.ts +1 -0
  48. package/dist/types/useOnElementResize.d.ts +1 -1
  49. package/dist/types/useReadMoreTruncate.d.ts +1 -1
  50. package/dist/types/utils.d.ts +16 -0
  51. package/package.json +10 -8
  52. package/dist/cjs/DSReadMoreDefinitions.js.map +0 -7
  53. package/dist/esm/DSReadMoreDefinitions.js.map +0 -7
  54. package/dist/types/DSReadMoreDefinitions.d.ts +0 -12
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/useReadMoreTruncate.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport { useOnElementResize } from './useOnElementResize.js';\nimport type { DSReadMoreT } from './react-desc-prop-types.js';\nimport { ScreenReaderOnly } from './styled.js';\n\n// offsetHeight + 2 takes into consideration any size of the button\nconst overflows = ({ offsetHeight, scrollHeight }: { offsetHeight: number; scrollHeight: number }) =>\n offsetHeight + 2 < scrollHeight;\n\n// Searchs the optimum cut on the text to have the button in the same line\nconst binSearchTextOverflow = (element: HTMLElement, parent: HTMLElement, content: string) => {\n const text = element.innerText;\n let left = 1;\n let right = text.length;\n let textEnd = 0;\n while (left <= right) {\n const middle = (left + right) / 2;\n element.innerText = content.slice(0, middle);\n if (overflows(parent)) right = middle - 1;\n else {\n left = middle + 1;\n textEnd = middle;\n }\n }\n element.innerText = content.slice(0, textEnd);\n};\n\nexport const useReadMoreTruncate = ({ lines, content }: Pick<DSReadMoreT.InternalProps, 'lines' | 'content'>) => {\n const textWrapperRef = useRef<HTMLSpanElement>(null);\n const textRef = useRef<HTMLSpanElement>(null);\n\n const [expanded, setExpanded] = useState(false);\n\n const [showButton, setShowButton] = useState(false);\n\n // We need to re-run the effect when the size of the container changes\n const { width, height } = useOnElementResize(textWrapperRef);\n\n useLayoutEffect(() => {\n const textElement = textRef.current;\n const parentElement = textWrapperRef.current;\n if (parentElement && textElement) {\n textElement.innerText = content;\n /**\n * PUI-16110 - [ReadMore] Autotriggered-Visually broken in safari + Sequoia\n * -----------------------------------\n * In Safari 17.4+ (macOS Sequoia), using `scrollHeight` or `offsetHeight` with line-clamp\n * can break the layout and, potentially, trigger infinite loops through ResizeObserver.\n *\n * Instead of relying on overflow-based measurements, we calculate the actual pixel width\n * of the text using a canvas and compare it with the visible width of the wrapper.\n *\n * This method is layout-safe, does not force reflow, and works reliably across browsers.\n */\n if (lines === 1) {\n const computed = getComputedStyle(textElement);\n const font = `${computed.fontWeight} ${computed.fontSize} ${computed.fontFamily}`;\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n let isTruncated = false;\n if (ctx) {\n ctx.font = font;\n const measuredWidth = ctx.measureText(content).width;\n isTruncated = measuredWidth > parentElement.clientWidth;\n }\n setShowButton(expanded || isTruncated);\n return;\n }\n\n const isOverflowing = overflows(parentElement);\n setShowButton(expanded || isOverflowing);\n if (!expanded && isOverflowing) {\n binSearchTextOverflow(textElement, parentElement, content);\n }\n }\n }, [lines, content, expanded, width, height]);\n\n const srOnlyText = useMemo(() => <ScreenReaderOnly>{content}</ScreenReaderOnly>, [content]);\n\n const textProps = useMemo(\n () => ({\n 'aria-hidden': true,\n }),\n [],\n );\n\n return {\n textWrapperRef,\n textRef,\n showButton,\n expanded,\n setExpanded,\n textProps,\n srOnlyText,\n };\n};\n"],
5
- "mappings": "AAAA,YAAY,WAAW;AC6EY;AA7EnC,SAAgB,iBAAiB,SAAS,QAAQ,gBAAgB;AAClE,SAAS,0BAA0B;AAEnC,SAAS,wBAAwB;AAGjC,MAAM,YAAY,CAAC,EAAE,cAAc,aAAa,MAC9C,eAAe,IAAI;AAGrB,MAAM,wBAAwB,CAAC,SAAsB,QAAqB,YAAoB;AAC5F,QAAM,OAAO,QAAQ;AACrB,MAAI,OAAO;AACX,MAAI,QAAQ,KAAK;AACjB,MAAI,UAAU;AACd,SAAO,QAAQ,OAAO;AACpB,UAAM,UAAU,OAAO,SAAS;AAChC,YAAQ,YAAY,QAAQ,MAAM,GAAG,MAAM;AAC3C,QAAI,UAAU,MAAM,EAAG,SAAQ,SAAS;AAAA,SACnC;AACH,aAAO,SAAS;AAChB,gBAAU;AAAA,IACZ;AAAA,EACF;AACA,UAAQ,YAAY,QAAQ,MAAM,GAAG,OAAO;AAC9C;AAEO,MAAM,sBAAsB,CAAC,EAAE,OAAO,QAAQ,MAA4D;AAC/G,QAAM,iBAAiB,OAAwB,IAAI;AACnD,QAAM,UAAU,OAAwB,IAAI;AAE5C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAE9C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAGlD,QAAM,EAAE,OAAO,OAAO,IAAI,mBAAmB,cAAc;AAE3D,kBAAgB,MAAM;AACpB,UAAM,cAAc,QAAQ;AAC5B,UAAM,gBAAgB,eAAe;AACrC,QAAI,iBAAiB,aAAa;AAChC,kBAAY,YAAY;AAYxB,UAAI,UAAU,GAAG;AACf,cAAM,WAAW,iBAAiB,WAAW;AAC7C,cAAM,OAAO,GAAG,SAAS,UAAU,IAAI,SAAS,QAAQ,IAAI,SAAS,UAAU;AAC/E,cAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,cAAM,MAAM,OAAO,WAAW,IAAI;AAClC,YAAI,cAAc;AAClB,YAAI,KAAK;AACP,cAAI,OAAO;AACX,gBAAM,gBAAgB,IAAI,YAAY,OAAO,EAAE;AAC/C,wBAAc,gBAAgB,cAAc;AAAA,QAC9C;AACA,sBAAc,YAAY,WAAW;AACrC;AAAA,MACF;AAEA,YAAM,gBAAgB,UAAU,aAAa;AAC7C,oBAAc,YAAY,aAAa;AACvC,UAAI,CAAC,YAAY,eAAe;AAC9B,8BAAsB,aAAa,eAAe,OAAO;AAAA,MAC3D;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,SAAS,UAAU,OAAO,MAAM,CAAC;AAE5C,QAAM,aAAa,QAAQ,MAAM,oBAAC,oBAAkB,mBAAQ,GAAqB,CAAC,OAAO,CAAC;AAE1F,QAAM,YAAY;AAAA,IAChB,OAAO;AAAA,MACL,eAAe;AAAA,IACjB;AAAA,IACA,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-misused-promises */\n/* eslint-disable max-statements */\n/* eslint-disable complexity */\n// useReadMoreTruncate.tsx\nimport React, { useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport { useOnElementResize } from './useOnElementResize.js';\nimport type { DSReadMoreT } from './react-desc-prop-types.js';\nimport { ScreenReaderOnly } from './styled.js';\nimport { isSafari } from './utils.js';\n\n// offsetHeight + margin/buffer to avoid subpixel errors\nconst overflows = ({ offsetHeight, scrollHeight }: { offsetHeight: number; scrollHeight: number }) =>\n offsetHeight + 4 < scrollHeight;\n\n/**\n * binSearchTextOverflow slices the content until there's no overflow.\n * It assumes `element` is the <span> node containing the text, and `parent`\n * is the wrapper with the height/clamp applied.\n */\nconst binSearchTextOverflow = (element: HTMLElement, parent: HTMLElement, content: string) => {\n let left = 0;\n let right = content.length;\n let textEnd = 0;\n\n // Place the full text before starting\n element.innerText = content;\n\n while (left <= right) {\n const mid = Math.floor((left + right) / 2);\n element.innerText = content.slice(0, mid);\n\n if (overflows(parent)) {\n // Still overflowing \u2192 reduce\n right = mid - 1;\n } else {\n // No overflow \u2192 we can increase\n textEnd = mid;\n left = mid + 1;\n }\n }\n\n element.innerText = content.slice(0, textEnd);\n};\n\nexport const useReadMoreTruncate = ({ lines, content }: Pick<DSReadMoreT.InternalProps, 'lines' | 'content'>) => {\n const textWrapperRef = useRef<HTMLSpanElement>(null);\n const textRef = useRef<HTMLSpanElement>(null);\n\n const [expanded, setExpanded] = useState(false);\n const [showButton, setShowButton] = useState(false);\n\n // Detect container size changes\n const { width, height } = useOnElementResize(textWrapperRef);\n\n useLayoutEffect(() => {\n const textEl = textRef.current;\n const wrapperEl = textWrapperRef.current;\n if (!textEl || !wrapperEl) return;\n\n // 1) Always reset the text to \"content\"\n textEl.innerText = content;\n\n // 2) Case lines === 1: measure with canvas\n if (lines === 1 && isSafari()) {\n const computed = getComputedStyle(textEl);\n const font = `${computed.fontWeight} ${computed.fontSize} ${computed.fontFamily}`;\n\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n let truncated = false;\n\n if (ctx) {\n ctx.font = font;\n const measuredWidth = ctx.measureText(content).width;\n truncated = measuredWidth > wrapperEl.clientWidth;\n }\n\n setShowButton(expanded || truncated);\n // No need to truncate char by char in single-line: hiding with CSS is enough.\n return;\n }\n\n // 3) For multiline: first check native overflow\n const isOverflowing = overflows({\n offsetHeight: wrapperEl.offsetHeight,\n scrollHeight: wrapperEl.scrollHeight,\n });\n setShowButton(expanded || isOverflowing);\n\n // 4) If not expanded and there is overflow, adjust the slice with binary search\n if (!expanded && isOverflowing) {\n // Function that executes binSearchTextOverflow\n const doBinaryTruncate = () => {\n if (!textEl || !wrapperEl) return;\n requestAnimationFrame(() => {\n binSearchTextOverflow(textEl, wrapperEl, content);\n });\n };\n\n // If the browser supports document.fonts, wait for them to load.\n if ('fonts' in document && document.fonts.ready) {\n document.fonts.ready\n .then(() => {\n doBinaryTruncate();\n })\n .catch(() => {\n // If fonts.ready fails, fall back to timeout\n setTimeout(doBinaryTruncate, 0);\n });\n } else {\n // Fallback for browsers without the Font Loading API\n setTimeout(doBinaryTruncate, 0);\n }\n }\n }, [lines, content, expanded, width, height]);\n\n // Hidden text for screen readers (always the full string)\n const srOnlyText = useMemo(() => <ScreenReaderOnly>{content}</ScreenReaderOnly>, [content]);\n\n // Mark the visible content as aria-hidden\n const textProps = useMemo(() => ({ 'aria-hidden': true }), []);\n\n return {\n textWrapperRef,\n textRef,\n showButton,\n expanded,\n setExpanded,\n textProps,\n srOnlyText,\n };\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACqHY;AAjHnC,SAAgB,iBAAiB,SAAS,QAAQ,gBAAgB;AAClE,SAAS,0BAA0B;AAEnC,SAAS,wBAAwB;AACjC,SAAS,gBAAgB;AAGzB,MAAM,YAAY,CAAC,EAAE,cAAc,aAAa,MAC9C,eAAe,IAAI;AAOrB,MAAM,wBAAwB,CAAC,SAAsB,QAAqB,YAAoB;AAC5F,MAAI,OAAO;AACX,MAAI,QAAQ,QAAQ;AACpB,MAAI,UAAU;AAGd,UAAQ,YAAY;AAEpB,SAAO,QAAQ,OAAO;AACpB,UAAM,MAAM,KAAK,OAAO,OAAO,SAAS,CAAC;AACzC,YAAQ,YAAY,QAAQ,MAAM,GAAG,GAAG;AAExC,QAAI,UAAU,MAAM,GAAG;AAErB,cAAQ,MAAM;AAAA,IAChB,OAAO;AAEL,gBAAU;AACV,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AAEA,UAAQ,YAAY,QAAQ,MAAM,GAAG,OAAO;AAC9C;AAEO,MAAM,sBAAsB,CAAC,EAAE,OAAO,QAAQ,MAA4D;AAC/G,QAAM,iBAAiB,OAAwB,IAAI;AACnD,QAAM,UAAU,OAAwB,IAAI;AAE5C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAGlD,QAAM,EAAE,OAAO,OAAO,IAAI,mBAAmB,cAAc;AAE3D,kBAAgB,MAAM;AACpB,UAAM,SAAS,QAAQ;AACvB,UAAM,YAAY,eAAe;AACjC,QAAI,CAAC,UAAU,CAAC,UAAW;AAG3B,WAAO,YAAY;AAGnB,QAAI,UAAU,KAAK,SAAS,GAAG;AAC7B,YAAM,WAAW,iBAAiB,MAAM;AACxC,YAAM,OAAO,GAAG,SAAS,UAAU,IAAI,SAAS,QAAQ,IAAI,SAAS,UAAU;AAE/E,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,YAAM,MAAM,OAAO,WAAW,IAAI;AAClC,UAAI,YAAY;AAEhB,UAAI,KAAK;AACP,YAAI,OAAO;AACX,cAAM,gBAAgB,IAAI,YAAY,OAAO,EAAE;AAC/C,oBAAY,gBAAgB,UAAU;AAAA,MACxC;AAEA,oBAAc,YAAY,SAAS;AAEnC;AAAA,IACF;AAGA,UAAM,gBAAgB,UAAU;AAAA,MAC9B,cAAc,UAAU;AAAA,MACxB,cAAc,UAAU;AAAA,IAC1B,CAAC;AACD,kBAAc,YAAY,aAAa;AAGvC,QAAI,CAAC,YAAY,eAAe;AAE9B,YAAM,mBAAmB,MAAM;AAC7B,YAAI,CAAC,UAAU,CAAC,UAAW;AAC3B,8BAAsB,MAAM;AAC1B,gCAAsB,QAAQ,WAAW,OAAO;AAAA,QAClD,CAAC;AAAA,MACH;AAGA,UAAI,WAAW,YAAY,SAAS,MAAM,OAAO;AAC/C,iBAAS,MAAM,MACZ,KAAK,MAAM;AACV,2BAAiB;AAAA,QACnB,CAAC,EACA,MAAM,MAAM;AAEX,qBAAW,kBAAkB,CAAC;AAAA,QAChC,CAAC;AAAA,MACL,OAAO;AAEL,mBAAW,kBAAkB,CAAC;AAAA,MAChC;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,SAAS,UAAU,OAAO,MAAM,CAAC;AAG5C,QAAM,aAAa,QAAQ,MAAM,oBAAC,oBAAkB,mBAAQ,GAAqB,CAAC,OAAO,CAAC;AAG1F,QAAM,YAAY,QAAQ,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC,CAAC;AAE7D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+ const isSafari = () => {
3
+ if (typeof navigator === "undefined") return false;
4
+ const ua = navigator.userAgent;
5
+ return /safari/i.test(ua) && !/chrome|chromium|android/i.test(ua);
6
+ };
7
+ export {
8
+ isSafari
9
+ };
10
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/utils.ts"],
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/**\n * Detects whether the current browser is Safari (excluding Chrome/Chromium and Android browsers).\n *\n * This utility is used to apply Safari-specific layout and styling workarounds.\n * Safari has known differences and bugs around text truncation, line-clamp,\n * and layout measurements (e.g. offsetHeight/scrollHeight), which can affect\n * components that rely on precise DOM sizing.\n *\n * The check is intentionally lightweight and user-agent based, as feature\n * detection is not reliable for these Safari-specific rendering issues.\n *\n * When used inside React components, this function should be evaluated once\n * (e.g. via useMemo) to avoid layout churn and focus instability caused by\n * re-evaluating browser detection on every render.\n */\nexport const isSafari = () => {\n if (typeof navigator === 'undefined') return false;\n\n const ua = navigator.userAgent;\n // Safari but not Chrome/Chromium/Android\n return /safari/i.test(ua) && !/chrome|chromium|android/i.test(ua);\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACehB,MAAM,WAAW,MAAM;AAC5B,MAAI,OAAO,cAAc,YAAa,QAAO;AAE7C,QAAM,KAAK,UAAU;AAErB,SAAO,UAAU,KAAK,EAAE,KAAK,CAAC,2BAA2B,KAAK,EAAE;AAClE;",
6
+ "names": []
7
+ }
@@ -4,7 +4,6 @@ interface MoreLessButtonProps {
4
4
  onClick?: () => void;
5
5
  ellipsis: string;
6
6
  withTooltip?: boolean;
7
- ariaLabel: string;
8
7
  dataTestId: string;
9
8
  getOwnerProps: () => object;
10
9
  getOwnerPropsArguments: () => object;
@@ -0,0 +1,33 @@
1
+ export declare const DSReadMoreName = "DSReadmore";
2
+ export declare const READ_MORE_SLOTS: {
3
+ readonly ROOT: "root";
4
+ readonly TEXT_CONTENT: "text-content";
5
+ readonly TOOLTIP_WRAPPER: "tooltip-wrapper";
6
+ readonly BUTTON_WRAPPER: "button-wrapper";
7
+ readonly BUTTON: "button";
8
+ };
9
+ export declare const DSReadMoreSlots: {
10
+ readonly ROOT: "root";
11
+ readonly TEXT_CONTENT: "text-content";
12
+ readonly TOOLTIP_WRAPPER: "tooltip-wrapper";
13
+ readonly BUTTON_WRAPPER: "button-wrapper";
14
+ readonly BUTTON: "button";
15
+ };
16
+ export declare const READ_MORE_DATA_TESTID: {
17
+ READ_MORE_BUTTON: string;
18
+ READ_MORE_TOOLTIP_BUTTON: string;
19
+ READ_MORE_TEXT: string;
20
+ ROOT: "ds-readmore-root";
21
+ TEXT_CONTENT: "ds-readmore-text-content";
22
+ TOOLTIP_WRAPPER: "ds-readmore-tooltip-wrapper";
23
+ BUTTON_WRAPPER: "ds-readmore-button-wrapper";
24
+ };
25
+ export declare const DSReadMoreDataTestIds: {
26
+ READ_MORE_BUTTON: string;
27
+ READ_MORE_TOOLTIP_BUTTON: string;
28
+ READ_MORE_TEXT: string;
29
+ ROOT: "ds-readmore-root";
30
+ TEXT_CONTENT: "ds-readmore-text-content";
31
+ TOOLTIP_WRAPPER: "ds-readmore-tooltip-wrapper";
32
+ BUTTON_WRAPPER: "ds-readmore-button-wrapper";
33
+ };
@@ -1,4 +1,4 @@
1
1
  export { DSReadMore, DSReadMoreWithSchema } from './DSReadMore.js';
2
- export { DSReadMoreName, DSReadMoreSlots, DSReadMoreDataTestIds } from './DSReadMoreDefinitions.js';
2
+ export { DSReadMoreName, DSReadMoreSlots, READ_MORE_SLOTS, DSReadMoreDataTestIds, READ_MORE_DATA_TESTID, } from './constants/index.js';
3
3
  export type { DSReadMoreT } from './react-desc-prop-types.js';
4
4
  export { useReadMoreTruncate } from './useReadMoreTruncate.js';
@@ -1,5 +1,6 @@
1
- import type { WeakValidationMap } from 'react';
2
- import type { GlobalAttributesT, XstyledProps, DSPropTypesSchema } from '@elliemae/ds-props-helpers';
1
+ import type { GlobalAttributesT, XstyledProps, DSPropTypesSchema, ValidationMap } from '@elliemae/ds-props-helpers';
2
+ import { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';
3
+ import { DSReadMoreName, READ_MORE_SLOTS } from './constants/index.js';
3
4
  export declare namespace DSReadMoreT {
4
5
  type PropsT<D, R, O, E> = Partial<D> & R & O & Omit<GlobalAttributesT<E>, keyof D | keyof R | keyof O> & XstyledProps;
5
6
  type InternalPropsT<D, R, O, E> = D & R & O & Omit<GlobalAttributesT<E>, keyof D | keyof R | keyof O> & XstyledProps;
@@ -12,7 +13,7 @@ export declare namespace DSReadMoreT {
12
13
  ellipsis: string;
13
14
  withTooltip: boolean;
14
15
  }
15
- export interface OptionalProps {
16
+ export interface OptionalProps extends TypescriptHelpersT.PropsForGlobalOnSlots<typeof DSReadMoreName, typeof READ_MORE_SLOTS> {
16
17
  }
17
18
  export interface RequiredProps {
18
19
  content: string;
@@ -23,4 +24,4 @@ export declare namespace DSReadMoreT {
23
24
  }
24
25
  export declare const defaultProps: DSReadMoreT.DefaultProps;
25
26
  export declare const DSReadMorePropTypes: DSPropTypesSchema<DSReadMoreT.Props>;
26
- export declare const DSReadMorePropTypesSchema: WeakValidationMap<DSReadMoreT.Props>;
27
+ export declare const DSReadMorePropTypesSchema: ValidationMap<DSReadMoreT.Props>;
@@ -1,15 +1,18 @@
1
- /// <reference types="react" />
2
1
  export declare const StyledTextWrapper: import("styled-components").StyledComponent<"span", import("@elliemae/ds-system").Theme, {
3
2
  lines: number;
4
3
  expanded: boolean;
4
+ isSafari: boolean;
5
5
  } & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"span">, never>;
6
6
  export declare const StyledTextContent: import("styled-components").StyledComponent<"span", import("@elliemae/ds-system").Theme, {
7
7
  lines: number;
8
8
  expanded: boolean;
9
+ isSafari: boolean;
9
10
  } & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"span">, never>;
10
11
  export declare const StyledTooltipWrapper: import("styled-components").StyledComponent<"div", import("@elliemae/ds-system").Theme, object & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"div">, never>;
11
- export declare const StyledButtonWrapper: import("styled-components").StyledComponent<"span", import("@elliemae/ds-system").Theme, object & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"span">, never>;
12
- export declare const StyledButton: import("styled-components").StyledComponent<import("react").ComponentType<import("@elliemae/ds-button-v2").DSButtonV2T.Props>, import("@elliemae/ds-system").Theme, {
13
- withTooltip?: boolean | undefined;
14
- } & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<import("react").ComponentType<import("@elliemae/ds-button-v2").DSButtonV2T.Props>>, never>;
12
+ export declare const StyledButtonWrapper: import("styled-components").StyledComponent<"span", import("@elliemae/ds-system").Theme, {
13
+ expanded: boolean;
14
+ } & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"span">, never>;
15
+ export declare const StyledButton: import("styled-components").StyledComponent<import("react").ComponentType<import("@elliemae/ds-button-v2").DSButtonV3T.Props>, import("@elliemae/ds-system").Theme, {
16
+ withTooltip?: boolean;
17
+ } & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<import("react").ComponentType<import("@elliemae/ds-button-v2").DSButtonV3T.Props>>, never>;
15
18
  export declare const ScreenReaderOnly: import("styled-components").StyledComponent<"span", import("@elliemae/ds-system").Theme, object & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"span">, never>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export const ControlledTestRenderer: import("react").FunctionComponent<import("react").JSX.IntrinsicAttributes>;
@@ -1,2 +1,2 @@
1
1
  import type React from 'react';
2
- export declare const useOnElementResize: <T extends HTMLElement>(targetRef: React.RefObject<T>) => Record<'width' | 'height', number>;
2
+ export declare const useOnElementResize: <T extends HTMLElement>(targetRef: React.RefObject<T>) => Record<"width" | "height", number>;
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import type { DSReadMoreT } from './react-desc-prop-types.js';
3
- export declare const useReadMoreTruncate: ({ lines, content }: Pick<DSReadMoreT.InternalProps, 'lines' | 'content'>) => {
3
+ export declare const useReadMoreTruncate: ({ lines, content }: Pick<DSReadMoreT.InternalProps, "lines" | "content">) => {
4
4
  textWrapperRef: React.RefObject<HTMLSpanElement>;
5
5
  textRef: React.RefObject<HTMLSpanElement>;
6
6
  showButton: boolean;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Detects whether the current browser is Safari (excluding Chrome/Chromium and Android browsers).
3
+ *
4
+ * This utility is used to apply Safari-specific layout and styling workarounds.
5
+ * Safari has known differences and bugs around text truncation, line-clamp,
6
+ * and layout measurements (e.g. offsetHeight/scrollHeight), which can affect
7
+ * components that rely on precise DOM sizing.
8
+ *
9
+ * The check is intentionally lightweight and user-agent based, as feature
10
+ * detection is not reliable for these Safari-specific rendering issues.
11
+ *
12
+ * When used inside React components, this function should be evaluated once
13
+ * (e.g. via useMemo) to avoid layout churn and focus instability caused by
14
+ * re-evaluating browser detection on every render.
15
+ */
16
+ export declare const isSafari: () => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elliemae/ds-read-more",
3
- "version": "3.53.0-alpha.1",
3
+ "version": "3.53.0-alpha.3",
4
4
  "license": "MIT",
5
5
  "description": "ICE MT - Dimsum - Read More",
6
6
  "files": [
@@ -36,16 +36,18 @@
36
36
  "indent": 4
37
37
  },
38
38
  "dependencies": {
39
- "@elliemae/ds-button-v2": "3.53.0-alpha.1",
40
- "@elliemae/ds-props-helpers": "3.53.0-alpha.1",
41
- "@elliemae/ds-system": "3.53.0-alpha.1",
42
- "@elliemae/ds-tooltip-v3": "3.53.0-alpha.1"
39
+ "@elliemae/ds-accessibility": "3.53.0-alpha.3",
40
+ "@elliemae/ds-button-v2": "3.53.0-alpha.3",
41
+ "@elliemae/ds-props-helpers": "3.53.0-alpha.3",
42
+ "@elliemae/ds-system": "3.53.0-alpha.3",
43
+ "@elliemae/ds-tooltip-v3": "3.53.0-alpha.3"
43
44
  },
44
45
  "devDependencies": {
45
- "@elliemae/pui-cli": "9.0.0-next.63",
46
+ "@elliemae/pui-cli": "9.0.0-next.65",
46
47
  "jest": "~29.7.0",
47
48
  "styled-components": "~5.3.9",
48
- "@elliemae/ds-monorepo-devops": "3.53.0-alpha.1"
49
+ "@elliemae/ds-monorepo-devops": "3.53.0-alpha.3",
50
+ "@elliemae/ds-test-utils": "3.53.0-alpha.3"
49
51
  },
50
52
  "peerDependencies": {
51
53
  "lodash-es": "^4.17.21",
@@ -59,7 +61,7 @@
59
61
  },
60
62
  "scripts": {
61
63
  "dev": "cross-env NODE_ENV=development node ../../../scripts/build/build.mjs --watch",
62
- "test": "pui-cli test --passWithNoTests --coverage=\"false\"",
64
+ "test": "playwright test -c ./playwright.config.mjs && pui-cli test --passWithNoTests --coverage=\"false\"",
63
65
  "lint": "node ../../../scripts/lint.mjs --fix",
64
66
  "lint:strict": "node ../../../scripts/lint-strict.mjs",
65
67
  "dts": "node ../../../scripts/dts.mjs",
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/DSReadMoreDefinitions.tsx", "../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["import { omit } from 'lodash-es';\nimport { slotObjectToDataTestIds } from '@elliemae/ds-system';\n\nexport const DSReadMoreName = 'DSReadmore';\n\nexport const DSReadMoreSlots = {\n ROOT: 'root',\n TEXT_CONTENT: 'text-content',\n TOOLTIP_WRAPPER: 'tooltip-wrapper',\n BUTTON_WRAPPER: 'button-wrapper',\n BUTTON: 'button',\n};\n\nexport const DSReadMoreDataTestIds = {\n ...omit(slotObjectToDataTestIds(DSReadMoreName, DSReadMoreSlots), 'BUTTON'),\n READ_MORE_BUTTON: 'ds-read_more-button',\n READ_MORE_TOOLTIP_BUTTON: 'ds-read_more-tooltip-button',\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAvB,uBAAqB;AACrB,uBAAwC;AAEjC,MAAM,iBAAiB;AAEvB,MAAM,kBAAkB;AAAA,EAC7B,MAAM;AAAA,EACN,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,QAAQ;AACV;AAEO,MAAM,wBAAwB;AAAA,EACnC,OAAG,2BAAK,0CAAwB,gBAAgB,eAAe,GAAG,QAAQ;AAAA,EAC1E,kBAAkB;AAAA,EAClB,0BAA0B;AAC5B;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/DSReadMoreDefinitions.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { omit } from 'lodash-es';\nimport { slotObjectToDataTestIds } from '@elliemae/ds-system';\n\nexport const DSReadMoreName = 'DSReadmore';\n\nexport const DSReadMoreSlots = {\n ROOT: 'root',\n TEXT_CONTENT: 'text-content',\n TOOLTIP_WRAPPER: 'tooltip-wrapper',\n BUTTON_WRAPPER: 'button-wrapper',\n BUTTON: 'button',\n};\n\nexport const DSReadMoreDataTestIds = {\n ...omit(slotObjectToDataTestIds(DSReadMoreName, DSReadMoreSlots), 'BUTTON'),\n READ_MORE_BUTTON: 'ds-read_more-button',\n READ_MORE_TOOLTIP_BUTTON: 'ds-read_more-tooltip-button',\n};\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,YAAY;AACrB,SAAS,+BAA+B;AAEjC,MAAM,iBAAiB;AAEvB,MAAM,kBAAkB;AAAA,EAC7B,MAAM;AAAA,EACN,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,QAAQ;AACV;AAEO,MAAM,wBAAwB;AAAA,EACnC,GAAG,KAAK,wBAAwB,gBAAgB,eAAe,GAAG,QAAQ;AAAA,EAC1E,kBAAkB;AAAA,EAClB,0BAA0B;AAC5B;",
6
- "names": []
7
- }
@@ -1,12 +0,0 @@
1
- export declare const DSReadMoreName = "DSReadmore";
2
- export declare const DSReadMoreSlots: {
3
- ROOT: string;
4
- TEXT_CONTENT: string;
5
- TOOLTIP_WRAPPER: string;
6
- BUTTON_WRAPPER: string;
7
- BUTTON: string;
8
- };
9
- export declare const DSReadMoreDataTestIds: {
10
- READ_MORE_BUTTON: string;
11
- READ_MORE_TOOLTIP_BUTTON: string;
12
- };