freemium-survey-components 2.0.484 → 2.0.485
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/adrs/.gitkeep +0 -0
- package/catalog-info.yaml +35 -0
- package/docs/adrs/index.md +21 -0
- package/docs/architecture/code-structure.md +118 -0
- package/docs/architecture/deep-dive.md +401 -0
- package/docs/getting-started.md +110 -0
- package/docs/index.md +57 -0
- package/docs/operations/alerts-configuration.md +19 -0
- package/docs/operations/how-to-debug.md +73 -0
- package/docs/operations/monitoring.md +26 -0
- package/docs/operations/recent-issues.md +22 -0
- package/docs/operations/sop.md +44 -0
- package/docs/operations/support.md +39 -0
- package/lib/bundle.css +1 -1
- package/mkdocs.yml +38 -0
- package/package.json +1 -1
package/adrs/.gitkeep
ADDED
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Prism (Backstage) software catalog descriptor.
|
|
2
|
+
# Registers this repo as the shared survey-rendering component library of the
|
|
3
|
+
# SurveyServ system. The `platforms/surveyserv` System entity itself is
|
|
4
|
+
# declared in the freshdesk/surveyserv repo (see PR #5448) — not redeclared
|
|
5
|
+
# here to avoid a duplicate-entity conflict in the catalog.
|
|
6
|
+
# Schema: `get_catalog_schema` in the Prism MCP server is the source of truth.
|
|
7
|
+
apiVersion: backstage.io/v1alpha1
|
|
8
|
+
kind: Component
|
|
9
|
+
metadata:
|
|
10
|
+
name: freemium-survey-components
|
|
11
|
+
namespace: platforms
|
|
12
|
+
title: Freemium Survey Components
|
|
13
|
+
description: >-
|
|
14
|
+
Shared React UI component library that renders the SurveyServ survey
|
|
15
|
+
collection form from a survey definition object. Published as an npm
|
|
16
|
+
package and consumed by SurveyServ frontends (admin, public widget,
|
|
17
|
+
public page).
|
|
18
|
+
annotations:
|
|
19
|
+
github.com/project-slug: freshdesk/freemium-survey-components
|
|
20
|
+
backstage.io/techdocs-ref: dir:.
|
|
21
|
+
tags:
|
|
22
|
+
- survey
|
|
23
|
+
- react
|
|
24
|
+
- library
|
|
25
|
+
- typescript
|
|
26
|
+
- ui-components
|
|
27
|
+
labels:
|
|
28
|
+
exposure: internal
|
|
29
|
+
language: TypeScript
|
|
30
|
+
orgVersion: v1alpha1
|
|
31
|
+
spec:
|
|
32
|
+
type: library
|
|
33
|
+
lifecycle: production
|
|
34
|
+
owner: platforms/plat-maise-surveyserv
|
|
35
|
+
system: platforms/surveyserv
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Architecture Decision Records
|
|
2
|
+
|
|
3
|
+
No ADRs are recorded for this repo today — there is no `adrs/`-sourced decision record, and no
|
|
4
|
+
dedicated architecture-decision page was found in Confluence space `SP` beyond general
|
|
5
|
+
architecture/roadmap docs (e.g. "Frontend Roadmap — Surveyserv (Backlog 2026)",
|
|
6
|
+
"freemium-survey-components — Engineering Improvement Brief") which describe planned work rather
|
|
7
|
+
than ratified decisions.
|
|
8
|
+
|
|
9
|
+
Notable architectural choices that exist in code but are not backed by a written ADR (call these
|
|
10
|
+
out as "decision, not yet documented as an ADR" if you're relying on them):
|
|
11
|
+
|
|
12
|
+
- Two branching mechanisms coexist (`dependent_blocks` and `display_logic` — see
|
|
13
|
+
[Deep Dive → Idiomatic vernacular](../architecture/deep-dive.md#4-idiomatic-vernacular)) rather
|
|
14
|
+
than one being fully migrated/removed.
|
|
15
|
+
- The library maintains three parallel layout implementations (`card`, `standard`, `widget`) with
|
|
16
|
+
duplicated `SurveyEndCard`/`PageNavigator`/`QuestionFooter` components per layout rather than a
|
|
17
|
+
shared abstraction (see [Deep Dive → Architecture & control flow](../architecture/deep-dive.md#1-architecture--control-flow)).
|
|
18
|
+
- Peer-dependency-only React (no bundling) via Rollup's `external` config (`rollup.config.ts`).
|
|
19
|
+
|
|
20
|
+
When a real ADR is written for this repo, add source files under `adrs/` at the repo root and
|
|
21
|
+
list/link them from this page.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Code Structure
|
|
2
|
+
|
|
3
|
+
This is the "where do I find X" map of `src/`, grouped by responsibility. Every file is real —
|
|
4
|
+
paths are exact as of this writing (see [Deep Dive → Staleness triggers](deep-dive.md#8-staleness-triggers)
|
|
5
|
+
for what invalidates this map).
|
|
6
|
+
|
|
7
|
+
## Entry points
|
|
8
|
+
|
|
9
|
+
| File | Description |
|
|
10
|
+
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| `src/index.ts` | The npm package entry point. Re-exports `src/components`, `src/survey`, `src/survey/meta-channel-preview`, `src/types`, and a few constants (`ALL_FONTS`, `DEFAULT_FONT`, `FONT_SIZE_OPTIONS`). Everything exported transitively from here is the library's public surface. |
|
|
12
|
+
| `src/index.d.ts` | Hand-written extra ambient type re-exports (`components/text-input/types`, `components/pickers/types`) that aren't otherwise picked up by the Rollup TS declaration build. |
|
|
13
|
+
|
|
14
|
+
## Survey rendering shell — `src/survey/`
|
|
15
|
+
|
|
16
|
+
The layout/orchestration layer: turns a survey + answers into a navigable flow. Each layout
|
|
17
|
+
(`card`, `standard`, `widget`) has its own React component plus a shared hook from `src/survey/utils/`.
|
|
18
|
+
|
|
19
|
+
| File | Description |
|
|
20
|
+
| ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
21
|
+
| `src/survey/index.tsx` | Top-level exported components: `Survey` (dispatches to `CardSurvey`/`StandardSurvey` based on `surveyStyle`), `WebInAppSurvey` (widget entry, shows a loader until `survey.id` exists), `QuestionPreview` (single-question, stateless preview used by the survey builder). |
|
|
22
|
+
| `src/survey/question.tsx` | `Question` — the single dispatcher that maps `question.type_info.question_type` to the concrete answer widget from `src/components/*`. This is the file to touch when adding a new question type. |
|
|
23
|
+
| `src/survey/card/index.tsx` | `CardSurvey` + `SurveyEndCard` — one-question-per-screen layout with animated transitions (used standalone and as the base for the widget). |
|
|
24
|
+
| `src/survey/standard/index.tsx` | `StandardSurvey` + `SurveyEndCard` — all-questions-on-one-page (classic form) layout, with paging support. |
|
|
25
|
+
| `src/survey/widget/index.tsx` | `Widget` — the floating/embedded chat-style widget shell (header, minimize/maximize, backdrop, unsubscribe footer) that wraps the widget-specific `Survey`. |
|
|
26
|
+
| `src/survey/widget/survey.tsx` | Widget-flavored `Survey` + `SurveyEndCard` (built on `useCardSurvey`) — adds widget-only concerns: dynamic height animation, progress bar, minimized-header text. |
|
|
27
|
+
| `src/survey/Links/index.tsx` | Renders the thank-you-page review/social/custom-button links block (`show_review_links`, `show_social_links`, `show_custom_button`). |
|
|
28
|
+
| `src/survey/end-card/*.tsx` | Thank-you visual variants: `CardThankYou`, `StandardThankYou`, `WidgetThankYou` (selected by `src/utils.tsx#thankYouComponent`). |
|
|
29
|
+
| `src/survey/survey.scss`, `src/survey/card/index.scss`, `src/survey/standard/index.scss`, `src/survey/widget/index.scss` | Layout-specific styles; consumed via Rollup's `postcss` plugin into `lib/bundle.css`. |
|
|
30
|
+
| `src/survey/*.stories.tsx`, `src/survey/SurveyStory.tsx`, `src/survey/WebInAppSurveyStory` equivalents | Storybook stories/fixtures for the top-level layouts. |
|
|
31
|
+
|
|
32
|
+
### Survey state & branching logic — `src/survey/utils/`
|
|
33
|
+
|
|
34
|
+
| File | Description |
|
|
35
|
+
| ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
36
|
+
| `src/survey/utils/use-survey.ts` | `useSurvey(survey, answers)` — normalizes a raw `SurveyType` into an `AugmentedSurveyType` (injects `question_names` into every block, deep-clones, filters out soft-deleted questions and their orphaned answers). Runs once per `survey.updated_at` change. Used by every layout entry point. |
|
|
37
|
+
| `src/survey/utils/use-card-survey.ts` | `useCardSurvey(props)` — all state/behavior for the card & widget layouts: rendered-block stack, pivot question/answer, per-statement matrix index, navigation, validation, submit. |
|
|
38
|
+
| `src/survey/utils/use-standard-survey.ts` | `useStandardSurvey(props)` — the standard-layout equivalent; additionally handles pages, "sets" (branch-separate-page groups), and pre-rendering multiple blocks at once (all questions visible, not just the active one). |
|
|
39
|
+
| `src/survey/utils/logics.ts` | ~2,800-line pure-function core: display logic evaluation (`evaluateConditions`, `getIsDisplay`), branching/skip navigation (`getNextLogicalBlock`, `fetchNextBlockIndex`, `getNextBlockIndex`), pivot-question resolution (`getPivotQuestion`, `getQuestionForBlock`), button enable/disable rules (`canDisplayButton`, `canDisableButton`), and field validators (`dateValidator`, `formValidator`, `textboxValidator`, `contactformValidator`, `fileUploadQnValidator`). See [Deep Dive → Architecture & control flow](deep-dive.md#1-architecture--control-flow). |
|
|
40
|
+
| `src/survey/utils/index.ts` | Barrel: re-exports `use-survey`, `use-card-survey`, and `logics` (note: `use-standard-survey` is **not** re-exported here — it's imported directly by `src/survey/standard/index.tsx`). |
|
|
41
|
+
|
|
42
|
+
### Chat-channel previews — `src/survey/meta-channel-preview/`
|
|
43
|
+
|
|
44
|
+
Renders the survey as it would appear inside WhatsApp / Instagram / Facebook Messenger / a generic
|
|
45
|
+
web-chat preview, for the collector-builder UI. Exported via `src/survey/meta-channel-preview` →
|
|
46
|
+
re-exported from `src/index.ts`.
|
|
47
|
+
|
|
48
|
+
| File | Description |
|
|
49
|
+
| ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
50
|
+
| `src/survey/meta-channel-preview/index.tsx` | `ChannelPreview` (dispatches to chat/facebook/whatsapp/instagram renderers) and `ChannelLinkPreview` (renders just the outbound message/link-preview card for a channel). |
|
|
51
|
+
| `src/survey/meta-channel-preview/question.tsx` | Channel-specific question rendering (large file, ~1,300 lines — presentation + branching combined). |
|
|
52
|
+
| `src/survey/meta-channel-preview/answer.tsx` | Renders a previously-given answer bubble in the chat transcript; one of only two files in `src` that runs `DOMPurify.sanitize` before `dangerouslySetInnerHTML`. |
|
|
53
|
+
| `src/survey/meta-channel-preview/facebook/index.tsx`, `web-chat.tsx`, `preview-channel.tsx`, `meta-components.tsx`, `utils.tsx`, `RatingTypeIcon.tsx` | Per-channel chrome (Messenger frame, WhatsApp/Instagram frame, shared header/bubble components, rating icon mapping). |
|
|
54
|
+
| `*.scss`, `*.svg` | Channel-specific chat chrome styling and static art (`thankyou.svg`, `whatsapp-chat-bg.svg`). |
|
|
55
|
+
|
|
56
|
+
## Question-type & shared UI components — `src/components/`
|
|
57
|
+
|
|
58
|
+
Each subfolder is one question-type widget or shared primitive. `src/components/index.tsx` is the
|
|
59
|
+
barrel that decides what's re-exported to `src/index.ts` (and therefore public).
|
|
60
|
+
|
|
61
|
+
| File/folder | Description |
|
|
62
|
+
| ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
63
|
+
| `src/components/index.tsx` | Barrel — re-exports `binary`, `Button`, `Checkbox`, `Dropdown`, `footer`, `Loader`, `PhoneNumberInput`, `DatePicker`, `DateTimePicker`, `progressbar`, `RadioGroup`, `range`, `rank-order`, `slider`, `survey-progress`, `text-input`. Anything **not** listed here (e.g. `matrix`, `matrix-widget`, `form-field`, `contact-form`, `file-qn`, `consent`, `number`, `textbox`, `questionComment`, `page-container`, `prompt`, `link-preview`, `icons`) is imported directly by path from `src/survey/question.tsx` and is **not** part of the npm package's public surface. |
|
|
64
|
+
| `src/components/binary/index.tsx` | `Binary` — Yes/No / thumbs-style boolean question (also used for `RESOLUTION` type). |
|
|
65
|
+
| `src/components/matrix/index.tsx`, `matrix-widget/index.tsx` | `Matrix` (standard/card layout) and `MatrixWidget` (mobile/widget layout, one statement at a time) for `MATRIX_RATING_SCALE`. |
|
|
66
|
+
| `src/components/range/index.tsx`, `range/utils.tsx` | `Range` — NPS/CSAT/CES point-scale question (emoji, star, number, text variants); exported directly from README's documented public API. |
|
|
67
|
+
| `src/components/rank-order/index.tsx` | `RankOrder` — drag-to-reorder question, built on `@dnd-kit`. |
|
|
68
|
+
| `src/components/slider/index.tsx` | `Slider` — numeric slider question, built on `@radix-ui/react-slider`. |
|
|
69
|
+
| `src/components/checkbox/*`, `radio-button/*`, `dropdown/index.tsx` | `CHECKBOX`/`MULTI_SELECT`, `RADIO`/`DROPDOWN` choice-question widgets, including "Others" free-text handling. |
|
|
70
|
+
| `src/components/text-input/*`, `textbox/index.tsx`, `number/index.tsx` | `TEXT`, `PARAGRAPH`, `NUMBER` question inputs. |
|
|
71
|
+
| `src/components/pickers/*` | `DatePicker`, `DateTimePicker` (`DATE`, `DATE_TIME` questions), built on `@blueprintjs/datetime2`; `constants.ts` holds `TIME_ZONE_SEPERATOR`. |
|
|
72
|
+
| `src/components/form-field/index.tsx`, `contact-form/index.tsx` | `FORM_FIELD` and `CONTACT_FORM` multi-field composite questions, each with its own per-field validators. |
|
|
73
|
+
| `src/components/file-qn/index.tsx` | `FILE` upload question, built on `filepond` + `@pqina/pintura` (image editor). |
|
|
74
|
+
| `src/components/consent/index.tsx` | `CONSENT` question (checkbox or button display format). |
|
|
75
|
+
| `src/components/questionComment/index.tsx` | The optional "add a comment" box attached to range/checkbox/radio/slider/boolean questions. |
|
|
76
|
+
| `src/components/matrix-widget`, `page-container`, `progressbar`, `survey-progress`, `prompt`, `link-preview`, `footer` | Layout/chrome primitives: paged-block accordion, progress bar/percentage, opt-in "prompt" screen shown before the survey starts, outbound link preview card, survey footer (branding/language dropdown). |
|
|
77
|
+
| `src/components/icons/index.tsx` | ~2,650-line SVG icon registry (`Icon` component + named icon components e.g. `WidgetClose`, `WidgetMaximize`, `WidgetMinimize`). |
|
|
78
|
+
| `src/components/button/index.tsx`, `Loader/index.tsx` | Shared `Button` (primary/secondary variants) and `Loader` spinner. |
|
|
79
|
+
| `*/style.scss` next to each component | Component-scoped styles, all rolled into the single `lib/bundle.css`. |
|
|
80
|
+
| `*.stories.tsx` next to relevant components | Component-level Storybook stories (`Button.stories.tsx`, `Checkbox.stories.tsx`, `Dropdown.stories.tsx`, `Radio.stories.tsx`, `RadioGroup.stories.tsx`, `DatePicker.stories.tsx`, `DateTimePicker.stories.tsx`, `Loader.stories.tsx`). |
|
|
81
|
+
|
|
82
|
+
## Types, constants, utils
|
|
83
|
+
|
|
84
|
+
| File | Description |
|
|
85
|
+
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
86
|
+
| `src/types.ts` | ~2,100-line single source of truth for every domain type: `SurveyType`, `SurveyQuestionType`, `CollectorType`, all the component prop types (`SurveyProps`, `WidgetProps`, `QuestionFooterProps`, …). See [Deep Dive → Idiomatic vernacular](deep-dive.md#4-idiomatic-vernacular) for the domain glossary. |
|
|
87
|
+
| `src/constants.ts` | Cross-cutting constants: `AUTO_COMMIT_FIELD_TYPES`, `SKIP_DISABLED_QUESTIONS`, animation durations (`CARD_ANIMATION_DURATION`, `TRANSITION_DURATION`), widget sizing tables (`WIDGET_MAX_HEIGHT`, `WIDGET_CONTAINER_WIDTH`), `EMOJI_PICK_HELPER`/`SCALE_TO_EMOJI` rating-to-icon maps, `REVIEW_PAGE_OPTIONS` (review-site logos for the thank-you page), and a full IANA `timeZones` lookup table. |
|
|
88
|
+
| `src/utils.tsx` | Cross-cutting pure helpers: `isNil`/`isEmpty`/`isRTL`, animation direction helpers (`initialAnimation`/`exitAnimation`), `resolvePlaceholders` (merges `{{...}}` placeholders + NPS/CSAT/CES rating tokens into question/thank-you text), `setCSSVariablesIfPresent` (writes theme colors as CSS custom properties on `document.documentElement`), `transformSurvey`, `getRangeToRender`, `appLogger`/`getFreshSurveyDebug` (debug logging gated by `localStorage`/`sessionStorage` flags), `setOrderForRenderedBlocks`. |
|
|
89
|
+
| `src/palette.ts` | Static brand color palette (hex values) used as CSS fallback defaults (e.g. `palette.S50` as a footer background fallback). |
|
|
90
|
+
| `src/global.d.ts`, `src/react-app-env.d.ts` | Ambient module declarations (SVG/SCSS imports, CRA env types). |
|
|
91
|
+
|
|
92
|
+
## Mocks & Storybook fixtures
|
|
93
|
+
|
|
94
|
+
| File | Description |
|
|
95
|
+
| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
96
|
+
| `src/mocks/index.ts` | `MOCK_SURVEY` and related fixture exports — a full realistic `SurveyType` payload used across Storybook stories and manual dev. Not part of the npm package's runtime purpose; dev/test-only. |
|
|
97
|
+
| `src/mocks/gallery.ts`, `src/mocks/fchatgallery.ts` | Large fixture data (`gallery.ts` alone is ~30,600 lines) for gallery/file-question and chat-channel stories. |
|
|
98
|
+
| `src/stories/*.stories.tsx` + matching `*Story.tsx` | One pair per question type/scenario (Boolean, Consent, DateTime, DateTimeZone, File, FormFields, Gallery, LinkPreview, LongTextAnswer, Matrix{Single,Multiple}, Mcq{Single,Multiple}, Message, Number, QuestionPreview, Range, RankOrder, Slider, Textbox) plus `Channel.stories.tsx` / `ChannelLinkPreview.stories.tsx` for the chat previews. `*Story.tsx` holds the reusable render body; `*.stories.tsx` holds the Storybook `Meta`/`StoryObj` wiring. |
|
|
99
|
+
| `src/stories/utils.ts`, `src/stories/style.scss` | Shared story helpers/styles. |
|
|
100
|
+
| `src/survey/*.stories.tsx` (`Survey.stories.tsx`, `WebInAppSurvey.stories.tsx`, `QuestionPreview.stories.tsx`) | Top-level layout stories. |
|
|
101
|
+
| `src/test/__snapshots__/*.snap` | Committed HTML snapshots asserted by the Storybook test-runner (see [Getting Started](../getting-started.md)). |
|
|
102
|
+
| `src/test/snapshot-resolver.js`, `src/test/snapshot-serializer.js` | Custom Jest snapshot resolver/serializer wiring referenced from `test-runner-jest.config.js`. |
|
|
103
|
+
|
|
104
|
+
## Build & tooling config
|
|
105
|
+
|
|
106
|
+
| File | Description |
|
|
107
|
+
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
108
|
+
| `rollup.config.ts` | The npm build: entry `src/index.ts` → `lib/index.cjs.js` (CJS) + `lib/index.esm.js` (ESM), both with `inlineDynamicImports: true` (single-chunk output); externalizes `react`/`react-dom`; extracts styles to `lib/bundle.css` via `rollup-plugin-postcss`; minifies via `rollup-plugin-terser`; type declarations via `rollup-plugin-typescript2`. |
|
|
109
|
+
| `tsconfig.json` | `strict: true` but `noImplicitAny` is not enabled — see [Deep Dive → Failure modes](deep-dive.md#6-failure-modes--sharp-edges). |
|
|
110
|
+
| `.babelrc`, `postcss.config.js` | Babel preset config (used by `react-scripts`/Storybook webpack path) and PostCSS preset-env config for the Rollup CSS extraction. |
|
|
111
|
+
| `scripts/buildUtils.js`, `scripts/frankBuild.js` | Supporting build scripts invoked around the Rollup build. |
|
|
112
|
+
| `.storybook/main.ts` | Storybook 8 config (webpack5 + CRA preset, addons: essentials, interactions, a11y, styling-webpack, Chromatic). |
|
|
113
|
+
| `.storybook/preview.ts`, `.storybook/manager.ts` | Storybook preview/manager UI customization. |
|
|
114
|
+
| `.storybook/test-runner.ts` | Configures `@storybook/test-runner`'s `postVisit` hook to snapshot each story's rendered `innerHTML` after animations settle (`TRANSITION_DURATION` / `WIDGET_MINIMIZE_ANIMATION_DURATION`). This is the repo's only automated test coverage — see [Deep Dive → Failure modes](deep-dive.md#6-failure-modes--sharp-edges). |
|
|
115
|
+
| `test-runner-jest.config.js` | Jest config layered on top of `@storybook/test-runner`'s default config; wires the custom snapshot resolver/serializer from `src/test/`. |
|
|
116
|
+
| `chromatic.config.json` | Chromatic (visual regression / Storybook publish) project config, used by `npm run chromatic`. |
|
|
117
|
+
| `.eslintignore`, `.prettierignore`, `.prettierrc` | Lint/format scope and formatting rules (`@trivago/prettier-plugin-sort-imports` enforces import ordering — do not hand-reorder imports). |
|
|
118
|
+
| `.husky/pre-commit`, `.husky/commit-msg` | Git hooks: pre-commit runs lint + format + install + full Rollup build (see [Deep Dive → Failure modes](deep-dive.md#6-failure-modes--sharp-edges) for why this is slow/bypassable). |
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
# Architecture Deep Dive
|
|
2
|
+
|
|
3
|
+
## 1. Architecture & control flow
|
|
4
|
+
|
|
5
|
+
**Exported entry points** (all re-exported from `src/index.ts`):
|
|
6
|
+
|
|
7
|
+
- `Survey` (`src/survey/index.tsx`) — full survey form, `surveyStyle: 'card' | 'standard'`.
|
|
8
|
+
- `WebInAppSurvey` (`src/survey/index.tsx`) — the floating/embedded widget entry (wraps `Widget` from `src/survey/widget`).
|
|
9
|
+
- `QuestionPreview` (`src/survey/index.tsx`) — stateless single-question preview (used by the survey builder to preview one question without running the full navigation engine).
|
|
10
|
+
- `ChannelPreview`, `ChannelLinkPreview` (`src/survey/meta-channel-preview/index.tsx`) — chat-channel (WhatsApp/Instagram/Facebook/generic web-chat) previews of a survey.
|
|
11
|
+
|
|
12
|
+
**How a survey object flows in and a response payload flows out** (using `Survey` as the
|
|
13
|
+
canonical path; `WebInAppSurvey`/`Widget` and `ChannelPreview` follow the same shape):
|
|
14
|
+
|
|
15
|
+
1. Host renders `<Survey survey={surveyObject} answers={priorAnswers} onSubmit={fn} .../>`.
|
|
16
|
+
2. `useSurvey(props.survey, props.answers)` (`src/survey/utils/use-survey.ts`) normalizes the raw
|
|
17
|
+
`SurveyType` into an `AugmentedSurveyType`: deep-clones it, injects a `question_names` array into
|
|
18
|
+
every block, filters out soft-deleted questions, and drops any prior answers that belonged to a
|
|
19
|
+
now-deleted question. This only re-runs when `survey.updated_at` changes.
|
|
20
|
+
3. `Survey` dispatches to `CardSurvey` or `StandardSurvey` based on `props.surveyStyle`. Each of
|
|
21
|
+
these calls its own hook — `useCardSurvey` or `useStandardSurvey` (`src/survey/utils/`) — which
|
|
22
|
+
owns **all** interaction state: `blocks`, `renderedBlocks` (the stack of blocks shown so far),
|
|
23
|
+
`activeIndexOfRenderedBlocks`, `answers` (`AnswersType`), per-field error state, the "pivot"
|
|
24
|
+
question/answer (the first rating question that downstream branching keys off), and submitting
|
|
25
|
+
state.
|
|
26
|
+
4. The active block's question is handed to `Question` (`src/survey/question.tsx`), a single
|
|
27
|
+
`switch (question.type_info.question_type)` dispatcher that renders the matching widget from
|
|
28
|
+
`src/components/*` (e.g. `RANGE` → `Range`, `MATRIX_RATING_SCALE` → `Matrix`/`MatrixWidget`,
|
|
29
|
+
`FILE` → `FileUpload`). Each widget calls `onChangeHandler(value, commitValue?)` on interaction.
|
|
30
|
+
5. `onChangeHandler` is wired (by the layout component, not by `Question`) to
|
|
31
|
+
`saveFormValues(block, value, commitDirtyValue)`, which updates `answers.formValues` in the hook's
|
|
32
|
+
state. `commitDirtyValue`/`commitValue` distinguishes "value changed" (e.g. typing) from
|
|
33
|
+
"value finalized, safe to evaluate navigation" (e.g. blur, click Next, or an
|
|
34
|
+
`AUTO_COMMIT_FIELD_TYPES` question like `RANGE`/`NPS`/`BOOLEAN`/`MESSAGE` that commits immediately —
|
|
35
|
+
see `src/constants.ts`).
|
|
36
|
+
6. An effect keyed on `[answers, blocks]` in the hook calls `canMoveToNextBlock` (`logics.ts`); if
|
|
37
|
+
true, it calls `fetchNextBlockIndex` → `getNextBlockIndex` → (for branching/multi-dependent-block
|
|
38
|
+
questions) `getNextLogicalBlock`, all in `src/survey/utils/logics.ts`. These pure functions
|
|
39
|
+
evaluate `dependent_blocks` / `display_logic` against the current answers to decide the next
|
|
40
|
+
block index, or `Infinity` if the survey should end (routes to the resolved thank-you block via
|
|
41
|
+
`getAppliedTYBlock`).
|
|
42
|
+
7. The resolved next block + its matching question (`getQuestionForBlock`, which also resolves
|
|
43
|
+
rating-group/category branching using the pivot answer) is appended to `renderedBlocks`, and
|
|
44
|
+
`activeIndexOfRenderedBlocks` advances. `onSubmitHandler('PARTIAL')` fires on every step (unless
|
|
45
|
+
at the end), so the host receives a live partial payload on every commit.
|
|
46
|
+
8. When there is no next block, `onSubmitHandler('COMPLETE')` runs `getCompletedAnswers` (drops
|
|
47
|
+
answers for blocks that were never actually rendered/reached) and calls
|
|
48
|
+
`props.onSubmit(data, callback, status, action)` — `data` is a `SurveyResponseType`
|
|
49
|
+
(`{ [questionName]: answer, others_meta: {...} }`), `status` is `'PARTIAL' | 'COMPLETE' |
|
|
50
|
+
'PARTIAL_COMPLETE'`, `action` is `'CREATE' | 'EDIT'` (edit when `canResubmit` navigated the user
|
|
51
|
+
back to redo the survey). The host **must** invoke the passed `callback()` once its own save
|
|
52
|
+
completes — the library uses it to flip `isSurveyCompleted`/`isSubmitting` and show the thank-you
|
|
53
|
+
card.
|
|
54
|
+
|
|
55
|
+
```mermaid
|
|
56
|
+
sequenceDiagram
|
|
57
|
+
participant Host as Host app (e.g. survey-public-widget)
|
|
58
|
+
participant Survey as Survey / CardSurvey / StandardSurvey
|
|
59
|
+
participant Hook as useCardSurvey / useStandardSurvey
|
|
60
|
+
participant Logics as logics.ts (pure functions)
|
|
61
|
+
participant Question as Question dispatcher
|
|
62
|
+
participant Widget as Question-type widget (Range, Matrix, ...)
|
|
63
|
+
|
|
64
|
+
Host->>Survey: <Survey survey answers onSubmit ... />
|
|
65
|
+
Survey->>Hook: useSurvey() normalize survey+answers
|
|
66
|
+
Hook->>Question: render active block's question
|
|
67
|
+
Question->>Widget: render matching widget
|
|
68
|
+
Widget->>Hook: onChangeHandler(value, commit)
|
|
69
|
+
Hook->>Hook: saveFormValues -> setAnswers
|
|
70
|
+
Hook->>Logics: canMoveToNextBlock / fetchNextBlockIndex
|
|
71
|
+
Logics-->>Hook: next block index (or Infinity)
|
|
72
|
+
Hook->>Host: onSubmit(data, callback, 'PARTIAL', action)
|
|
73
|
+
Host-->>Hook: callback() after host persists
|
|
74
|
+
Hook->>Question: render next block's question
|
|
75
|
+
Note over Hook,Logics: repeats until next block index is Infinity
|
|
76
|
+
Hook->>Host: onSubmit(completedData, callback, 'COMPLETE', action)
|
|
77
|
+
Host-->>Hook: callback() -> isSurveyCompleted = true
|
|
78
|
+
Hook->>Survey: render SurveyEndCard (thank-you)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`ChannelPreview`/`ChannelLinkPreview` follow the same `useSurvey` normalization but render a
|
|
82
|
+
read-only chat transcript instead of running the navigation engine — they call `onAnswer` per
|
|
83
|
+
question (`QuestionPreview`) rather than driving `onSubmit`.
|
|
84
|
+
|
|
85
|
+
## 2. Contracts & interfaces
|
|
86
|
+
|
|
87
|
+
Everything exported (directly or transitively) from `src/index.ts` is **PUBLIC** — consumers'
|
|
88
|
+
builds break if you change its shape. Everything else, including sibling files inside the same
|
|
89
|
+
folder that aren't re-exported, is **INTERNAL** and safe to refactor as long as the public surface's
|
|
90
|
+
behavior is preserved.
|
|
91
|
+
|
|
92
|
+
### Top-level components (`PUBLIC`)
|
|
93
|
+
|
|
94
|
+
| Export | Source | Props type | Inputs | Outputs / side effects | Error modes |
|
|
95
|
+
| -------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
96
|
+
| `Survey` | `src/survey/index.tsx` | `SurveyProps` (`src/types.ts`) | `survey: SurveyType`, `answers: SurveyResponseType \| null`, `surveyStyle?: 'standard'\|'card'`, `onSubmit`, `placeholders?`, `preview?`, `isMobile?`, `onAnsweringPrompt`, `initialPivotAnswer?`, `initialQuestionAnswer?`, `onBlockChange?`, `getSurveyProgress?`, `footerProps?` | Calls `onSubmit(data, callback, status, action)` on every commit and on completion; calls `onBlockChange`/`getSurveyProgress` as navigation happens; writes CSS custom properties onto `document.documentElement` for theme colors and the WhatsApp chat background asset URL | Renders `null` if the active block/question can't be resolved (e.g. malformed survey with no valid first block) — no thrown exception; logs `console.error` for unknown `question_type` via `Question` |
|
|
97
|
+
| `WebInAppSurvey` | `src/survey/index.tsx` | `WidgetProps` (`src/types.ts`) | `survey`, `answers`, `onSubmit`, `onDismiss`, `onClose?`, `surveyType: 'default'\|'compact'\|'cozy'`, `placeholders`, `isLoading?`, `loadSurveyCollapsed?`, `unsubscribeUrl?`, `isSurveyCompleted`, `hideMinimize?` | Same `onSubmit` contract as `Survey`; calls `onDismiss`/`onClose` on user dismiss/unmount | Shows a `Loader` while `isLoading`/no `survey.id`; renders `null` if there's no `survey.id` and not loading |
|
|
98
|
+
| `QuestionPreview` | `src/survey/index.tsx` | `QuestionPreviewProps` | `question: SurveyQuestionType`, `block: SurveyBlockType`, `surveyStyle`, `onAnswer?`, `theme?`, `language?` | Calls `onAnswer({ value, block, question })` per interaction; **does not** call `onSubmit` — it is not survey-navigation aware | None known — pure render of one question |
|
|
99
|
+
| `ChannelPreview` | `src/survey/meta-channel-preview/index.tsx` | `SurveyProps & { channel: PreviewChannelType; previewConfig?: PreviewConfigType }` | `channel: 'chat'\|'facebook'\|'whatsapp'\|'instagram'` (others unsupported) | Renders a chat transcript; no `onSubmit`-style callback contract | `console.error` + renders `null` for unsupported `channel` values |
|
|
100
|
+
| `ChannelLinkPreview` | `src/survey/meta-channel-preview/index.tsx` | `LinkPreviewProps` | `channel: 'whatsapp'\|'instagram'\|'facebook'`, `meta_image?`, `meta_title?`, `short_url?`, `message?` | Pure render | `console.error` + renders `null` for unsupported `channel` |
|
|
101
|
+
|
|
102
|
+
### Standalone components re-exported via `src/components` (`PUBLIC`)
|
|
103
|
+
|
|
104
|
+
| Export | Source | Notes |
|
|
105
|
+
| ------------------------------ | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
106
|
+
| `Range` | `components/range/index.tsx` | Props: `{ question: SurveyQuestionType; onChangeHandler; value?; isWidget?; isMobile?; widgetStyle?; initialPivotAnswer?; isInitialTransition? }`. `> [UNVERIFIED] The README documents a different, older prop shape (a bare type_info object instead of question: SurveyQuestionType) — the current source is authoritative; the README example will not type-check against the current code.` |
|
|
107
|
+
| `Binary` | `components/binary/index.tsx` | NPS/CSAT boolean-style question widget; also used for `RESOLUTION` question type from `Question`. |
|
|
108
|
+
| `Button` | `components/button/index.tsx` | Shared primary/secondary button. |
|
|
109
|
+
| `Checkbox` | `components/checkbox/index.tsx` | Single checkbox primitive (not the `CHECKBOX` question group — that's `CheckboxGroup`, internal). |
|
|
110
|
+
| `Dropdown` | `components/dropdown/index.tsx` | Generic dropdown primitive. |
|
|
111
|
+
| `SurveyFooter` | `components/footer/index.tsx` | Survey/widget footer (branding, language switcher, footer content). |
|
|
112
|
+
| `Loader` | `components/Loader/index.tsx` | Spinner shown while `WebInAppSurvey` loads. |
|
|
113
|
+
| `PhoneNumberInput` | `components/phone-number-input/index.tsx` | Wraps `react-phone-number-input`. |
|
|
114
|
+
| `DatePicker`, `DateTimePicker` | `components/pickers/` | `DATE` / `DATE_TIME` question inputs; also usable standalone. |
|
|
115
|
+
| `ProgressBar` | `components/progressbar/index.tsx` | Generic step progress bar. |
|
|
116
|
+
| `RadioGroup` | `components/radio-button/radio-group.tsx` | `RADIO`/`DROPDOWN`-format choice group. |
|
|
117
|
+
| `RankOrder` | `components/rank-order/index.tsx` | Drag-to-reorder question. |
|
|
118
|
+
| `Slider` | `components/slider/index.tsx` | Numeric slider question. |
|
|
119
|
+
| `SurveyProgress` | `components/survey-progress/index.tsx` | Progress indicator variant used by standard/card layouts. |
|
|
120
|
+
| `Input`, `TextArea` | `components/text-input/index.tsx` | Generic text input/textarea primitives. |
|
|
121
|
+
|
|
122
|
+
### Types (`PUBLIC` — all of `src/types.ts` is re-exported)
|
|
123
|
+
|
|
124
|
+
The type surface is large (~140 exported types/interfaces). The load-bearing ones a consumer or
|
|
125
|
+
contributor actually touches:
|
|
126
|
+
|
|
127
|
+
| Type | Role |
|
|
128
|
+
| -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
129
|
+
| `SurveyType` | The full survey definition (questions, blocks, pages, theme, meta) — the primary input to `Survey`/`WebInAppSurvey`. |
|
|
130
|
+
| `SurveyResponseType` | The answer payload shape, both as input (`answers` prop) and output (`onSubmit`'s first argument). |
|
|
131
|
+
| `SurveyQuestionType`, `SurveyQuestionTypeInfo`, `QuestionType` | A single question and its type-specific config; `QuestionType` is the discriminant `Question` (`src/survey/question.tsx`) switches on. |
|
|
132
|
+
| `SurveyBlockType`, `DependentBlockType`, `DisplayLogicType` | Navigation graph: one block per question (or per rating-branch category), its `dependent_blocks` (old-style branching) and/or `display_logic` (new-style show/hide rules) — see [Section 5](#5-state-data--invariants). |
|
|
133
|
+
| `OnSubmitType` | `(data: SurveyResponseType, callback: Function, answerType: AnswerStatus, action: ComponentActions) => void` — the exact `onSubmit` contract every host must implement. |
|
|
134
|
+
| `AnswerStatus` | `'PARTIAL' \| 'COMPLETE' \| 'PARTIAL_COMPLETE'` — status flag hosts branch on to decide whether to persist as final. |
|
|
135
|
+
| `SurveyProps`, `WidgetProps`, `QuestionPreviewProps` | Prop contracts for the three top-level components. |
|
|
136
|
+
| `CollectorType` and its nested `*InfoType`s | Read-only context the widget consumes (e.g. `web_in_app_channel_info.backdrop_color`) — this library never mutates or creates collectors. |
|
|
137
|
+
|
|
138
|
+
### Internal-only building blocks (`INTERNAL`)
|
|
139
|
+
|
|
140
|
+
`useCardSurvey`, `useStandardSurvey`, everything in `src/survey/utils/logics.ts`
|
|
141
|
+
(`getNextLogicalBlock`, `fetchNextBlockIndex`, `canDisplayButton`, `canDisableButton`, the
|
|
142
|
+
validators, etc.), `Question` (`src/survey/question.tsx`), and every component under
|
|
143
|
+
`src/components/*` **not** listed in `src/components/index.tsx` (e.g. `Matrix`, `MatrixWidget`,
|
|
144
|
+
`FormField`, `ContactForm`, `FileUpload`, `ConsentQuestion`, `NumberQuestion`, `Textbox`,
|
|
145
|
+
`CommentBox`, `Icon`) are internal implementation detail. They can be freely refactored (renamed,
|
|
146
|
+
resplit, re-signatured) as long as the public components above keep behaving the same way.
|
|
147
|
+
|
|
148
|
+
## 3. Dependencies & coupling
|
|
149
|
+
|
|
150
|
+
**Prism entity:** [`platforms/component/freemium-survey-components`](https://prism.freshtools.ai/catalog/platforms/component/freemium-survey-components)
|
|
151
|
+
|
|
152
|
+
### Depends on
|
|
153
|
+
|
|
154
|
+
| Dependency | Kind | Why |
|
|
155
|
+
| --------------------------------------------------------------------------------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
156
|
+
| `react`, `react-dom` | Peer dependency (`package.json`: `>=16.0.2`) | Host must supply these — the library is externalized in the Rollup build (`external = /^react\|react-dom/`), never bundled. |
|
|
157
|
+
| `@blueprintjs/core`, `@blueprintjs/datetime2` | Direct dependency | Date/time picker primitives. |
|
|
158
|
+
| `@dnd-kit/*` | Direct dependency | Drag-and-drop for `RankOrder`. |
|
|
159
|
+
| `@radix-ui/react-*` | Direct dependency | Accessible primitives for checkbox/radio/select/slider. |
|
|
160
|
+
| `@pqina/*`, `filepond*`, `react-filepond` | Direct dependency | File-upload question UI + in-browser image editing. |
|
|
161
|
+
| `framer-motion` | Direct dependency | All block/question transition animations. |
|
|
162
|
+
| `moment-timezone` | Direct dependency | Date/time-zone parsing and comparison in `logics.ts` display-logic evaluation and the date pickers. |
|
|
163
|
+
| `dompurify` | Direct dependency | HTML sanitization — but only wired in 2 of the 20 `dangerouslySetInnerHTML` sites in `src` (see [Section 6](#6-failure-modes--sharp-edges)). |
|
|
164
|
+
| `react-phone-number-input`, `react-time-picker`, `react-transition-group`, `lodash.clonedeep` | Direct dependency | Phone input, time picker, transition groups, and the deep-clone used by `useSurvey`. |
|
|
165
|
+
| `lodash` | **Undeclared transitive dependency** | `import { isNil } from 'lodash'` in `src/components/binary/index.tsx`, `src/components/pickers/DateTimePicker.tsx`, `src/survey/meta-channel-preview/answer.tsx` — works today only because something else in the dependency tree pulls `lodash` in; not listed in `package.json`. |
|
|
166
|
+
|
|
167
|
+
### Depended on by
|
|
168
|
+
|
|
169
|
+
All four are real, `package.json`-evidenced npm consumers in the same Prism system
|
|
170
|
+
(`platforms/surveyserv`) — each npm-installs `freemium-survey-components` and renders the survey
|
|
171
|
+
form for a different surface:
|
|
172
|
+
|
|
173
|
+
| Consumer | Prism entity | Reason |
|
|
174
|
+
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
175
|
+
| `survey-public-widget` | [`platforms/component/survey-public-widget`](https://prism.freshtools.ai/catalog/platforms/component/survey-public-widget) | Renders the survey collection form for the public web-in-app widget (`freemium-survey-components@^2.0.483`). |
|
|
176
|
+
| `survey-public-page` | [`platforms/component/survey-public-page`](https://prism.freshtools.ai/catalog/platforms/component/survey-public-page) | Renders the survey collection form for the standalone public survey page (`^2.0.480`). |
|
|
177
|
+
| `freshsurvey-web-admin-app` | [`platforms/component/freshsurvey-web-admin-app`](https://prism.freshtools.ai/catalog/platforms/component/freshsurvey-web-admin-app) | Renders survey/question previews inside the survey builder/admin UI (`^2.0.126`). |
|
|
178
|
+
| `survey-admin-microfrontend` | [`platforms/component/survey-admin-microfrontend`](https://prism.freshtools.ai/catalog/platforms/component/survey-admin-microfrontend) | Renders survey/question previews inside the admin micro-frontend (`^2.0.484`). |
|
|
179
|
+
|
|
180
|
+
Because every consumer pins a `^`-range version independently, **this repo cannot assume all
|
|
181
|
+
consumers are on the latest published version** — a breaking change to a `PUBLIC` export
|
|
182
|
+
(Section 2) can land weeks or months before every consumer upgrades. `freshsurvey-web-admin-app`
|
|
183
|
+
in particular is pinned three major-ish versions behind (`^2.0.126` vs. current `2.0.484`).
|
|
184
|
+
|
|
185
|
+
### Hidden coupling
|
|
186
|
+
|
|
187
|
+
- **CSS custom properties as an implicit contract.** `setCSSVariablesIfPresent` (`src/utils.tsx`)
|
|
188
|
+
writes theme colors (from `survey.theme`) onto `document.documentElement` as `--fsc-*` CSS
|
|
189
|
+
variables (e.g. `--fsc-whatsapp-chat-background`, `--fsc-max-range-question-width`,
|
|
190
|
+
`--fsc-question-max-width`). Consumers that render this library alongside their own CSS must not
|
|
191
|
+
collide with the `--fsc-*` namespace, and anything that reads survey theming outside this library
|
|
192
|
+
(e.g. a host wrapper) is implicitly coupled to these exact variable names.
|
|
193
|
+
- **Global DOM IDs.** The rendered tree always uses fixed IDs: `#freemium-survey`,
|
|
194
|
+
`#freshworks-survey-widget`, `#surveyserv-widget-container`, `#surveyserv-widget-header`. Only one
|
|
195
|
+
instance of `Survey`/`WebInAppSurvey` can exist per page without ID collisions — this is an
|
|
196
|
+
unwritten single-instance-per-page assumption.
|
|
197
|
+
- **`localStorage`/`sessionStorage` as an implicit side channel.** `removeUploadedFile(localStorage)`
|
|
198
|
+
(called from `Survey`'s mount effect and from `resetSurvey`) clears file-upload keys from
|
|
199
|
+
`localStorage`; `getFreshSurveyDebug`/`appLogger` read debug flags from `localStorage`/
|
|
200
|
+
`sessionStorage` (e.g. `fsc:debug` — see `src/utils.tsx`). A host embedding multiple SurveyServ
|
|
201
|
+
widgets on one page shares this storage.
|
|
202
|
+
- **Peer-dependency version assumptions.** `package.json` declares `react`/`react-dom: >=16.0.2`,
|
|
203
|
+
but `package-lock.json` resolves and tests against React **17.0.2**, while `@types/react`/
|
|
204
|
+
`@types/react-dom` are pinned to **18.x**. The library has never been tested against React 16 or
|
|
205
|
+
19 in CI (there is no CI test job at all — see [Section 6](#6-failure-modes--sharp-edges)).
|
|
206
|
+
`> [UNVERIFIED] Whether the library actually works correctly on React 18/19 concurrent features (automatic batching, etc.) has not been verified by any automated test — it is inferred only from the fact that no consumer has reported breakage.`
|
|
207
|
+
- **`window`/`document`/`matchMedia` guards are inconsistent.** Some effects guard for SSR
|
|
208
|
+
(`typeof window === 'undefined'`), others (e.g. `scrollToNextBlock` in `use-standard-survey.ts`)
|
|
209
|
+
assume `document` exists unconditionally. Consumers that server-render must render this library
|
|
210
|
+
client-only.
|
|
211
|
+
|
|
212
|
+
## 4. Idiomatic vernacular
|
|
213
|
+
|
|
214
|
+
**Naming conventions, with real examples:**
|
|
215
|
+
|
|
216
|
+
- **`use*Survey` hooks own all state for one layout.** `useSurvey` (normalization, always run),
|
|
217
|
+
`useCardSurvey` (card + widget layouts), `useStandardSurvey` (standard layout). Copy
|
|
218
|
+
`src/survey/utils/use-card-survey.ts` as the template for "a hook that fully owns navigation state
|
|
219
|
+
for a layout."
|
|
220
|
+
- **`*Type` suffix for every domain type**, e.g. `SurveyType`, `CollectorType`, `AnswersType`,
|
|
221
|
+
`QuestionAnswerType` (`src/types.ts`). Prop-bag types for a component use `*Props`
|
|
222
|
+
(`SurveyProps`, `WidgetProps`, `CardQuestionFooterProps`).
|
|
223
|
+
- **`get*`/`is*`/`can*` prefixes in `logics.ts` signal pure, side-effect-free functions** that take
|
|
224
|
+
named-object args and return a value: `getNextLogicalBlock`, `getPivotQuestion`, `isBlockFinal`,
|
|
225
|
+
`isThankYouBlock`, `canDisplayButton`, `canDisableButton`, `canMoveToNextBlock`,
|
|
226
|
+
`canCommitDirtyValue`. When adding new branching logic, follow this pattern — a same-named `set*`
|
|
227
|
+
or component-coupled function in this file would be out of place.
|
|
228
|
+
- **`SurveyEndCard`, `PageNavigator`, `QuestionFooter` are re-implemented per layout**, not shared
|
|
229
|
+
components — `src/survey/card/index.tsx`, `src/survey/standard/index.tsx`, and
|
|
230
|
+
`src/survey/widget/survey.tsx` each define their own local versions with the same names but
|
|
231
|
+
different JSX/behavior. Do not assume "the same name across layout files" means "the same
|
|
232
|
+
component" — check the file.
|
|
233
|
+
- **`ss-*` / `svs-*` / `fsc-*` CSS class and variable prefixes** mark class names/CSS variables that
|
|
234
|
+
are part of the informal styling contract with hosts (e.g. `ss-question-container`,
|
|
235
|
+
`svs-icon`, `--fsc-question-max-width`). `freemium-survey-*` and `surveyserv-*` prefix the
|
|
236
|
+
top-level wrapper classes (`freemium-survey-components`, `surveyserv-widget-container`).
|
|
237
|
+
|
|
238
|
+
**Domain glossary** (as this codebase specifically uses the term — check `src/types.ts` and
|
|
239
|
+
`src/survey/utils/logics.ts` for the authoritative shape):
|
|
240
|
+
|
|
241
|
+
| Term | Meaning here |
|
|
242
|
+
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
243
|
+
| **Block** | One navigable step in the survey graph (`SurveyBlockType`). Usually maps 1:1 to a question (`block.question_name`), but a _rating-branch_ block instead has `category: SurveyBlockCategoryType[]` — one entry per pivot-answer range, each pointing at a different `question_name`. `BlockWithQuestionType` is a block with its resolved `question` attached; `renderedBlocks` is the ordered list of blocks actually shown to the user so far (as opposed to `blocks`, the full survey graph). |
|
|
244
|
+
| **Pivot question** | The rating question (NPS/CSAT/CES `RANGE`, or `BOOLEAN`) whose answer determines which _category_ branch of a downstream block is shown. Resolved by `getPivotQuestion`: first tries a question flagged `type_info.pivot_question`, then the first block's question, then the first `RANGE` question, then falls back to `questions[0]`. |
|
|
245
|
+
| **Dependent block / display logic** | Two overlapping branching mechanisms on `SurveyBlockType`/`SurveyQuestionType`: `dependent_blocks` (older, per-question `next_block` rules keyed by `choice_id`/`condition_type`/`operator`) and `display_logic` (newer, per-block/per-page `SHOW`/`HIDE` rule sets combined with `AND`/`OR`). Both are evaluated by the same `evaluateConditions` comparator in `logics.ts`. New branching features should extend `display_logic` rather than `dependent_blocks` unless the older behavior is specifically needed. |
|
|
246
|
+
| **Set** (`orderOfSet`, `branch_separate_page`) | A sub-grouping of blocks _within_ a page, used only in the standard layout when `survey.meta.branch_separate_page` is true — lets one page reveal blocks in waves (e.g. a rating question, then its follow-up, on the same page but staged). Tracked via `setOrderForRenderedBlocks` (`src/utils.tsx`) and `activeIndexOfSet`. |
|
|
247
|
+
| **Channel** | In `meta-channel-preview`, a chat delivery surface (`'whatsapp' \| 'instagram' \| 'facebook' \| 'chat'`) being previewed — unrelated to `CollectorChannelType` (`EMAIL`, `WEB_APP`, `SLACK`, …), which is the backend's delivery-channel enum for a `CollectorType`. Do not conflate the two "channel" concepts. |
|
|
248
|
+
| **Collector** | _Not_ rendered or created by this library — `CollectorType` is read-only context passed in (e.g. widget backdrop styling from `collector.channel_info.web_in_app_channel_info`). The collector lifecycle (scheduling, throttling, audience) lives in the backend. |
|
|
249
|
+
| **Commit / commitDirtyValue** | Whether an answer change should be treated as "final for this interaction" and trigger navigation evaluation now, vs. just updating local state (e.g. still typing). Threaded through `saveFormValues(block, value, commitDirtyValue)` and `canCommitDirtyValue` (auto-true for `AUTO_COMMIT_FIELD_TYPES` unless a comment box is attached). |
|
|
250
|
+
| **Widget** vs **Survey** vs **Channel preview** | Three distinct rendering _modes_ for the same question set: `Survey` (full-page/card form), `Widget`/`WebInAppSurvey` (floating embedded chat-style box), `ChannelPreview` (read-only chat transcript mockup). They share `Question` and most `components/*` widgets but have separate layout/state code. |
|
|
251
|
+
|
|
252
|
+
## 5. State, data & invariants
|
|
253
|
+
|
|
254
|
+
**What's mutated, and who owns it:**
|
|
255
|
+
|
|
256
|
+
| State | Owner | Notes |
|
|
257
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
258
|
+
| `answers: AnswersType` (`{ formValues, others, commitDirtyValue?, moveToNextPage? }`) | `useCardSurvey` / `useStandardSurvey` (one instance per mounted `Survey`) | The single source of truth for in-progress answers. Mutated only via `setAnswers` inside the owning hook — never directly by a component. `formValues` maps `question.name -> QuestionAnswerType`; `others` maps `question.name -> "Others" free-text value`. |
|
|
259
|
+
| `renderedBlocks: BlockWithQuestionType[]` | Same hook | The navigation stack: which blocks have been shown, in order. Card/widget layouts show only the last entry (`activeIndexOfRenderedBlocks`); standard layout renders all of them (paged by `activePage`). |
|
|
260
|
+
| `pivot` (`{ question, answer }`, a `useRef`) | Same hook | Deliberately a ref, not state — read by branching logic without forcing a re-render on every pivot update. |
|
|
261
|
+
| Validation errors (`ValidatingQuestions`: `formQuestionErrors`, `dateQuestionErrors`, `textboxQuestionErrors`, `fileUploadErrors`, `contactFormErrors`) | Same hook, via `setErrors` passed down to `Question` | Populated by the `*Validator` functions in `logics.ts`, invoked from the hook's `isValidQuestion`, and also directly by `DatePicker`/`FileUpload` calling `setErrors` on blur/upload-error. |
|
|
262
|
+
| CSS custom properties on `document.documentElement` | `setCSSVariablesIfPresent` (`src/utils.tsx`), called from layout `useEffect`s keyed on `survey.theme` | Global, page-wide mutation — see [Section 3 → Hidden coupling](#3-dependencies--coupling). |
|
|
263
|
+
| `localStorage` file-upload keys | `removeUploadedFile` (`src/survey/utils/index.ts` re-export), called on mount and on `resetSurvey` | Shared across any survey instance on the same origin. |
|
|
264
|
+
|
|
265
|
+
**Invariants** (violate these and expect silent wrong-navigation or a blank render, not a thrown error):
|
|
266
|
+
|
|
267
|
+
- `blocks` passed into the navigation engine always excludes the block named `'TY'` and any
|
|
268
|
+
`is_deleted` block (`use-card-survey.ts`/`use-standard-survey.ts` filter this on every `setBlocks`
|
|
269
|
+
call) — the thank-you block is resolved separately via `getAppliedTYBlock`, never addressed by
|
|
270
|
+
index like normal blocks.
|
|
271
|
+
- Every block in `blocks` must resolve to a `question` via `getQuestionForBlock`; if it can't
|
|
272
|
+
(e.g. `pivotQn` undefined and the block is a rating-branch block), the calling component returns
|
|
273
|
+
`null` rather than throwing — `Survey`/`CardSurvey`/`StandardSurvey`/`Survey` (widget) all early-return
|
|
274
|
+
`null` when `!activeBlock || !activeBlock.question`.
|
|
275
|
+
- `answers.formValues[questionName] === null` means **explicitly skipped**, not "unanswered" —
|
|
276
|
+
`undefined` means unanswered. `isCurrentBlockSkipped` and the deleted-question filter in
|
|
277
|
+
`useSurvey` both depend on this distinction; do not conflate `null` and `undefined` when writing
|
|
278
|
+
new code that touches `formValues`.
|
|
279
|
+
- `BOOLEAN` and `RESOLUTION` question answers are always coerced to `Boolean(value)` before being
|
|
280
|
+
stored (`saveFormValues` in both hooks) — a falsy-but-defined value (e.g. `0`) is normalized to
|
|
281
|
+
`false`, not stored as-is.
|
|
282
|
+
- `props.survey`/`props.answers` are effectively **read once per identity change**: the hooks cache
|
|
283
|
+
derived state keyed on `survey.updated_at` (and re-derive `blocks`/`pages` only when it changes).
|
|
284
|
+
The README's documented "Rules" section states props are cached on mount and the component must be
|
|
285
|
+
remounted to pick up a new survey — in the current code this is more precisely "cached until
|
|
286
|
+
`survey.updated_at` changes," which is a materially different (weaker) guarantee than "remount
|
|
287
|
+
required." Treat the README's blanket "must remount" statement as historical/imprecise;
|
|
288
|
+
`> [UNVERIFIED] whether every prop (not just survey/answers) is safe to update without remounting has not been exhaustively checked.`
|
|
289
|
+
|
|
290
|
+
## 6. Failure modes & sharp edges
|
|
291
|
+
|
|
292
|
+
- **Zero automated unit/behavioral test coverage.** `find src -iname '*.test.*' -o -iname '*.spec.*'`
|
|
293
|
+
returns 0 files, and `npm test` (`react-scripts test`) has nothing to run. The only automated
|
|
294
|
+
check is the Storybook test-runner's `innerHTML` snapshot per story (`.storybook/test-runner.ts` +
|
|
295
|
+
`src/test/__snapshots__/*.snap`) — it catches unintended markup/DOM changes, but **not** logic
|
|
296
|
+
regressions in `logics.ts` branching, validators, or anything not exercised by an existing story.
|
|
297
|
+
A change to `getNextLogicalBlock`/`evaluateConditions` can silently break real survey branching
|
|
298
|
+
and every snapshot will still pass. **Do not treat green Storybook snapshots as proof that
|
|
299
|
+
navigation/branching logic still works.**
|
|
300
|
+
- **No CI gate on pull requests.** `.github/workflows/` only has `security_verification.yml`
|
|
301
|
+
(Codepot scan), `pr-template-validation.yml`, `stale.yml`, `enhanced-pr-labeler.yml`, and
|
|
302
|
+
`automated-version-bump.yml` — none run `npm run lint`, `npm run build`, or `npm test` on a PR.
|
|
303
|
+
The only local enforcement is `.husky/pre-commit`, which runs lint + format + `npm install` + a
|
|
304
|
+
full `npm run build` on every commit and is bypassable with `git commit --no-verify`. A broken
|
|
305
|
+
Rollup build can be merged to `main` undetected until the next manual `npm run build` or publish.
|
|
306
|
+
- **`lodash` is an undeclared dependency.** `src/components/binary/index.tsx`,
|
|
307
|
+
`src/components/pickers/DateTimePicker.tsx`, and `src/survey/meta-channel-preview/answer.tsx`
|
|
308
|
+
`import { isNil } from 'lodash'`, but `lodash` is not in `package.json` — it currently resolves
|
|
309
|
+
only because something else in the dependency tree happens to pull it in. A future dependency bump
|
|
310
|
+
that drops that transitive `lodash` will break the build with no local signal until then.
|
|
311
|
+
- **Inconsistent HTML sanitization.** 20 `dangerouslySetInnerHTML` sites exist in `src`; only 2
|
|
312
|
+
(`src/components/link-preview/index.tsx`, `src/survey/meta-channel-preview/answer.tsx`) run
|
|
313
|
+
`DOMPurify.sanitize` first. Others — including `question.type_info.footer_text` and
|
|
314
|
+
`survey.footer_content` rendering in `src/survey/question.tsx` / `src/components/footer/index.tsx`
|
|
315
|
+
— render platform-supplied HTML unsanitized. `dompurify` is already a direct dependency; **don't
|
|
316
|
+
add a new `dangerouslySetInnerHTML` site without sanitizing it**, and treat existing unsanitized
|
|
317
|
+
sites as trusted only because the backend is assumed to control that content today.
|
|
318
|
+
- **`README.md`'s documented contracts have drifted from the code.** Two concrete, verified
|
|
319
|
+
mismatches: (1) the README's `Range` usage example passes a bare `type_info` object, but the
|
|
320
|
+
current `RangeProps` requires `question: SurveyQuestionType` (see [Section 2](#2-contracts--interfaces));
|
|
321
|
+
(2) the README states peer deps are `react`/`react-dom >= 17.0.2`, but `package.json` declares
|
|
322
|
+
`>=16.0.2`. **When README and code disagree, the code (`src/`, `package.json`) is authoritative** —
|
|
323
|
+
update the README rather than trusting it for a new integration.
|
|
324
|
+
- **Peer-dependency version drift is untested.** `package-lock.json` resolves React to `17.0.2` for
|
|
325
|
+
local dev/build, `@types/react`/`@types/react-dom` are pinned to `18.x`, and the declared peer
|
|
326
|
+
range is `>=16.0.2` with no upper bound — yet there is no CI test matrix across React versions.
|
|
327
|
+
A consumer on React 19 or a consumer still on React 16 is relying on behavior nobody has verified
|
|
328
|
+
automatically here.
|
|
329
|
+
- **`src/survey/utils/logics.ts` (2,793 lines) is the highest-risk file to change.** It mixes pure
|
|
330
|
+
branching/navigation rules with per-question-type validators in one module with no test coverage.
|
|
331
|
+
Small changes to `evaluateConditions`, `getNextBlockIndex`, or `canDisplayButton` can silently
|
|
332
|
+
change which button is shown/enabled or which block comes next for _every_ question type and
|
|
333
|
+
layout, because all three layouts (`card`, `standard`, `widget`) call into the same functions.
|
|
334
|
+
**Don't touch `logics.ts` without manually exercising all three layouts against a survey that
|
|
335
|
+
exercises both `dependent_blocks` and `display_logic` branching**, since there is no test suite to
|
|
336
|
+
catch a regression.
|
|
337
|
+
- **`src/components/icons/index.tsx` (2,653 lines) and `src/types.ts` (2,107 lines) are large,
|
|
338
|
+
frequently-touched files** — expect merge conflicts when multiple PRs add icons or types
|
|
339
|
+
concurrently; there's no per-domain split today.
|
|
340
|
+
- **Rollup output is a single inlined chunk per format** (`inlineDynamicImports: true`), currently
|
|
341
|
+
~839 KB gzipped JS + ~42 KB gzipped CSS. There is no bundle-size regression check in CI — a large
|
|
342
|
+
new dependency (e.g. another date library, another icon set) silently increases every consumer's
|
|
343
|
+
bundle.
|
|
344
|
+
- **`src/mocks/gallery.ts` is ~30,600 lines of fixture data**, committed to the repo and shipped in
|
|
345
|
+
git history (though excluded from the npm package via `.npmignore`'s `src`/`mock` entries) — be
|
|
346
|
+
aware of this when doing repo-wide search/refactor tooling; it will dominate line-count-based
|
|
347
|
+
tooling output.
|
|
348
|
+
- **Single-instance-per-page DOM ID assumptions** (`#freemium-survey`, `#freshworks-survey-widget`,
|
|
349
|
+
etc. — see [Section 3](#3-dependencies--coupling)) mean two `Survey`/`WebInAppSurvey` instances
|
|
350
|
+
mounted simultaneously on the same page will collide on IDs; this has not been a supported
|
|
351
|
+
configuration.
|
|
352
|
+
|
|
353
|
+
## 7. Change guidance
|
|
354
|
+
|
|
355
|
+
- **Adding a new question type** requires changes in lockstep across: `src/types.ts`
|
|
356
|
+
(`QuestionType` union + any new `type_info` fields), `src/survey/question.tsx` (new `switch` case
|
|
357
|
+
dispatching to a new/existing widget), a new component under `src/components/<name>/` (following
|
|
358
|
+
the `index.tsx` + `style.scss` + optional `types.ts`/`utils.ts` pattern — e.g. copy
|
|
359
|
+
`src/components/slider/` as a template for a simple typed-value widget), `src/constants.ts` if the
|
|
360
|
+
type needs to join `AUTO_COMMIT_FIELD_TYPES`/`SKIP_DISABLED_QUESTIONS`/`STRING_BASED_QUESTIONS`,
|
|
361
|
+
and `src/survey/utils/logics.ts` if the type needs bespoke validation (`isValidQuestion` switch in
|
|
362
|
+
both `use-card-survey.ts` and `use-standard-survey.ts`) or participates in `evaluateConditions`'s
|
|
363
|
+
`BETWEEN`/date-specific branches. Add a Storybook story under `src/stories/` (a `*Story.tsx` +
|
|
364
|
+
`*.stories.tsx` pair) so the new type gets snapshot coverage.
|
|
365
|
+
- **Changing branching/navigation behavior** (`logics.ts`) must be verified by hand across all three
|
|
366
|
+
layouts (`CardSurvey`, `StandardSurvey`, the widget `Survey` in `survey/widget/survey.tsx`) and
|
|
367
|
+
both branching mechanisms (`dependent_blocks` and `display_logic`) — there is no automated test to
|
|
368
|
+
catch a regression (see [Section 6](#6-failure-modes--sharp-edges)).
|
|
369
|
+
- **Changing a `PUBLIC` export's prop shape** (Section 2) is a breaking change for whichever of the
|
|
370
|
+
four dependent repos haven't upgraded yet (Section 3) — bump `minor`/`major` accordingly via the
|
|
371
|
+
version-bump workflow (see [Getting Started](../getting-started.md)) and call it out explicitly in
|
|
372
|
+
the PR's "Breaking Changes" section.
|
|
373
|
+
- **Running the existing coverage before/after a change:**
|
|
374
|
+
- Storybook visually + interactively: `npm run storybook` (build: `npm run build-storybook`).
|
|
375
|
+
- Snapshot tests: `npm run test-storybook` (uses `test-runner-jest.config.js`, drives
|
|
376
|
+
`.storybook/test-runner.ts`'s `postVisit` hook against every story in `src/**/*.stories.tsx`,
|
|
377
|
+
diffs against `src/test/__snapshots__/*.snap`). Requires Storybook to be built/served first per
|
|
378
|
+
`@storybook/test-runner`'s normal flow.
|
|
379
|
+
- Type/build check: `npm run build` (Rollup + `rollup-plugin-typescript2`) — the closest thing to
|
|
380
|
+
a compile-time regression check; run it before opening a PR since CI does not run it for you.
|
|
381
|
+
- `npm run lint` / `npm run format` — enforced locally via `.husky/pre-commit`, not in CI.
|
|
382
|
+
|
|
383
|
+
## 8. Staleness triggers
|
|
384
|
+
|
|
385
|
+
This document should be treated as stale and re-verified against source if any of the following change:
|
|
386
|
+
|
|
387
|
+
- `src/index.ts`'s export list, or `src/components/index.tsx`'s re-export list (changes the `PUBLIC`
|
|
388
|
+
surface in [Section 2](#2-contracts--interfaces)).
|
|
389
|
+
- The `QuestionType` union in `src/types.ts`, or the `switch` in `src/survey/question.tsx`.
|
|
390
|
+
- The signatures of `getNextLogicalBlock`, `fetchNextBlockIndex`, `getNextBlockIndex`,
|
|
391
|
+
`evaluateConditions`, `canDisplayButton`, `canDisableButton` in `src/survey/utils/logics.ts`.
|
|
392
|
+
- `AUTO_COMMIT_FIELD_TYPES`, `SKIP_DISABLED_QUESTIONS`, `STRING_BASED_QUESTIONS` in `src/constants.ts`.
|
|
393
|
+
- `package.json`'s `peerDependencies`, `dependencies` (especially any `lodash` declaration change),
|
|
394
|
+
or the Rollup `external`/output config in `rollup.config.ts`.
|
|
395
|
+
- Any of the four dependent repos' `package.json` `freemium-survey-components` version pin
|
|
396
|
+
(changes the "Depended on by" version-skew note in [Section 3](#3-dependencies--coupling)) —
|
|
397
|
+
re-check with `grep freemium-survey-components package.json` in each sibling repo.
|
|
398
|
+
- Addition of any test job to `.github/workflows/` or any `*.test.*`/`*.spec.*` file under `src`
|
|
399
|
+
(invalidates the "zero test coverage" claims in [Section 6](#6-failure-modes--sharp-edges)).
|
|
400
|
+
- `.github/workflows/automated-version-bump.yml`'s trigger/branch/publish logic (see
|
|
401
|
+
[Getting Started](../getting-started.md)).
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
- Node.js 18 (matches `actions/setup-node@v4` `node-version: '18'` used in
|
|
6
|
+
`.github/workflows/automated-version-bump.yml`).
|
|
7
|
+
- npm (repo uses `package-lock.json`, not yarn/pnpm).
|
|
8
|
+
- Peer dependencies your consuming app must already provide: `react` and `react-dom`
|
|
9
|
+
(`package.json` declares `>=16.0.2`; the lockfile resolves/tests against `17.0.2` locally — see
|
|
10
|
+
[Deep Dive → Failure modes](architecture/deep-dive.md#6-failure-modes--sharp-edges) for the
|
|
11
|
+
version-drift caveat).
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Local development (Storybook)
|
|
20
|
+
|
|
21
|
+
Storybook is the primary local dev loop for this component library — there is no standalone
|
|
22
|
+
"run the app" entry point.
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
npm run storybook # starts Storybook dev server on port 6006
|
|
26
|
+
npm run build-storybook # produces a static Storybook build (storybook-static/)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Stories live under `src/stories/**/*.stories.tsx` and `src/survey/**/*.stories.tsx`; see
|
|
30
|
+
[Architecture → Code Structure](architecture/code-structure.md) for the full map. Mock survey
|
|
31
|
+
data used across stories comes from `src/mocks/index.ts` (`MOCK_SURVEY`) and `src/mocks/gallery.ts`.
|
|
32
|
+
|
|
33
|
+
`npm start` (`react-scripts start`) also exists in `package.json` but this repo has no
|
|
34
|
+
CRA `public/index.html`-driven app to serve beyond Storybook's own tooling — Storybook is the
|
|
35
|
+
supported way to see components render.
|
|
36
|
+
|
|
37
|
+
## Build (the npm package artifact)
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
npm run build
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Runs `rollup -c rollup.config.ts`, producing `lib/index.cjs.js`, `lib/index.esm.js`,
|
|
44
|
+
`lib/bundle.css`, and `lib/types/` (see `rollup.config.ts` for the exact plugin chain). This is
|
|
45
|
+
also what `prepublishOnly` runs automatically before `npm publish`.
|
|
46
|
+
|
|
47
|
+
## Test
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
npm test # react-scripts test — currently has no *.test.*/*.spec.* files to run
|
|
51
|
+
npm run test-storybook # Storybook test-runner: snapshots each story's rendered innerHTML
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`npm run test-storybook` requires Storybook to be running/built first (standard
|
|
55
|
+
`@storybook/test-runner` flow: start `npm run storybook` or `npm run build-storybook` +
|
|
56
|
+
`http-server`, then run the test-runner against it). Snapshots are stored under
|
|
57
|
+
`src/test/__snapshots__/*.snap` with a custom resolver/serializer
|
|
58
|
+
(`src/test/snapshot-resolver.js`, `src/test/snapshot-serializer.js`) wired via
|
|
59
|
+
`test-runner-jest.config.js`. See
|
|
60
|
+
[Deep Dive → Failure modes](architecture/deep-dive.md#6-failure-modes--sharp-edges) for what this
|
|
61
|
+
test suite does and does not catch.
|
|
62
|
+
|
|
63
|
+
## Lint & format
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
npm run lint # eslint . --fix --quiet
|
|
67
|
+
npm run check # prettier --check .
|
|
68
|
+
npm run format # prettier --write .
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`.husky/pre-commit` runs lint + format + `npm install` + a full `npm run build` on every commit —
|
|
72
|
+
expect commits to take noticeably longer than the lint/format step alone; see
|
|
73
|
+
[Deep Dive → Failure modes](architecture/deep-dive.md#6-failure-modes--sharp-edges).
|
|
74
|
+
|
|
75
|
+
## Chromatic (visual regression)
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
npm run chromatic # publishes Storybook build to Chromatic
|
|
79
|
+
npm run chromatic-dry # dry run, no publish
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Configured via `chromatic.config.json`.
|
|
83
|
+
|
|
84
|
+
## Versioning & npm publish flow
|
|
85
|
+
|
|
86
|
+
This library ships no running service — its "release" is an npm package publish, automated by
|
|
87
|
+
`.github/workflows/automated-version-bump.yml`:
|
|
88
|
+
|
|
89
|
+
1. **Trigger a version bump** — manually via `workflow_dispatch` on that workflow, choosing
|
|
90
|
+
`version_type: patch | minor | major` (default `patch`).
|
|
91
|
+
2. The workflow checks out `main`, runs `npm version <type> --no-git-tag-version` to bump
|
|
92
|
+
`package.json`/`package-lock.json`, commits that bump on a new `version-bump-<timestamp>`
|
|
93
|
+
branch, and opens a PR titled `chore(release): bump version to <version>` back into `main`,
|
|
94
|
+
auto-populated with the commit log since the last tag.
|
|
95
|
+
3. **Merging that PR into `main`** triggers the workflow's `publish-after-merge` job (on
|
|
96
|
+
`push` to `main`): it detects the version-bump commit/branch pattern, runs `npm ci` +
|
|
97
|
+
`npm run build`, then `npm publish` (using `NPM_TOKEN`), creates and pushes a `v<version>` git
|
|
98
|
+
tag, deletes the now-merged `version-bump-*` branch, generates release notes from the commit
|
|
99
|
+
log, and creates a GitHub Release. It also posts a Slack notification if `SLACK_WEBHOOK_URL` is
|
|
100
|
+
configured (best-effort, `continue-on-error: true`).
|
|
101
|
+
4. There is also a manual escape hatch in `package.json`: `npm run version-beta` (
|
|
102
|
+
`npm version prerelease --preid=beta`) + `npm run publish-beta` (`npm publish --tag beta`) for
|
|
103
|
+
publishing an unlisted beta build without going through the automated flow.
|
|
104
|
+
|
|
105
|
+
Every consumer pins a `^`-range on `freemium-survey-components` in its own `package.json` — a
|
|
106
|
+
version bump does not immediately propagate to `survey-public-widget`, `survey-public-page`,
|
|
107
|
+
`freshsurvey-web-admin-app`, or `survey-admin-microfrontend` until each of those repos bumps its
|
|
108
|
+
own dependency. See [Deep Dive → Dependencies & coupling](architecture/deep-dive.md#3-dependencies--coupling)
|
|
109
|
+
for the current version skew across consumers, and [Deep Dive → Change guidance](architecture/deep-dive.md#7-change-guidance)
|
|
110
|
+
for what warrants a `minor`/`major` bump vs. `patch`.
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Freemium Survey Components
|
|
2
|
+
|
|
3
|
+
`freemium-survey-components` is the shared React/TypeScript UI component library that renders the
|
|
4
|
+
SurveyServ survey collection form from a survey definition object (`SurveyType`). It is published to
|
|
5
|
+
npm as `freemium-survey-components` and consumed in-process (not over the network) by every SurveyServ
|
|
6
|
+
frontend that needs to show a survey to an end user or preview it in an admin/builder context.
|
|
7
|
+
|
|
8
|
+
## Purpose & boundary
|
|
9
|
+
|
|
10
|
+
This repo owns the **rendering** of a survey: turning a `SurveyType` + prior `SurveyResponseType`
|
|
11
|
+
into a navigable, animated question flow (`Survey`, `WebInAppSurvey`, `QuestionPreview`,
|
|
12
|
+
`ChannelPreview`), collecting per-question answers, running client-side display/skip logic
|
|
13
|
+
(`src/survey/utils/logics.ts`), running field-level validation, and calling back to the host
|
|
14
|
+
application via `onSubmit` with the accumulated answers. It ships every question-type widget
|
|
15
|
+
(text, matrix, range/NPS, file upload, contact form, date/time, slider, rank order, consent, etc.)
|
|
16
|
+
and the card/standard/widget/channel-preview layout shells around them.
|
|
17
|
+
|
|
18
|
+
**Not responsible for:**
|
|
19
|
+
|
|
20
|
+
- **Persisting responses or surveys.** The library never calls a network API; it only invokes the
|
|
21
|
+
`onSubmit` / `onFileUpload` callbacks the host passes in. Persistence, auth, and the survey CRUD
|
|
22
|
+
API live in the SurveyServ backend (`surveyserv` repo), not here.
|
|
23
|
+
- **Deciding which survey/collector to show, when, or to whom.** Targeting, scheduling, throttling
|
|
24
|
+
and channel delivery are collector/backend concerns (`CollectorType`, `ScheduleInfoType`,
|
|
25
|
+
`ThrottleInfoType` in `src/types.ts` are consumed as read-only data, never computed here).
|
|
26
|
+
`src/mocks/` only exists for Storybook and this repo's own manual dev loop.
|
|
27
|
+
- **Hosting/serving the survey to an end user.** There is no routing, no server, no page shell.
|
|
28
|
+
That belongs to the four consumer apps — see [Deep Dive → Dependencies & coupling](architecture/deep-dive.md#3-dependencies--coupling).
|
|
29
|
+
- **A network API of its own.** This is a UI component library; its only "contract" is its exported
|
|
30
|
+
components/props (see [Deep Dive → Contracts & interfaces](architecture/deep-dive.md#2-contracts--interfaces)).
|
|
31
|
+
- **Independent deployment.** There is no running service or environment for this repo — it is
|
|
32
|
+
built with Rollup and published to npm; see [Getting Started](getting-started.md) for the
|
|
33
|
+
publish/version-bump flow.
|
|
34
|
+
|
|
35
|
+
## Tech & runtime
|
|
36
|
+
|
|
37
|
+
| Aspect | Value |
|
|
38
|
+
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
39
|
+
| Language(s) | TypeScript (`^4.9.5`), SCSS |
|
|
40
|
+
| Framework(s) | React (component library), Rollup (bundler), Storybook 8 (dev/docs/visual testing), `@storybook/test-runner` + Jest (snapshot testing) |
|
|
41
|
+
| Runtime / version constraints | Peer deps: `react` / `react-dom` `>=16.0.2` per `package.json` (README states `>=17.0.2` — treat `package.json` as authoritative; see [Deep Dive → Failure modes](architecture/deep-dive.md#6-failure-modes--sharp-edges)). Node 18 is used in CI/publish workflows. Ships as CJS (`lib/index.cjs.js`) and ESM (`lib/index.esm.js`) bundles plus `lib/bundle.css`, with type declarations in `lib/types`. |
|
|
42
|
+
|
|
43
|
+
## What's here
|
|
44
|
+
|
|
45
|
+
| Section | What you'll find |
|
|
46
|
+
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
47
|
+
| [Getting Started](getting-started.md) | Local install/build/run/test/Storybook workflow, peer dependencies, and the npm publish/version-bump flow |
|
|
48
|
+
| [Architecture → Code Structure](architecture/code-structure.md) | File-by-file map of `src/` grouped by responsibility |
|
|
49
|
+
| [Architecture → Deep Dive](architecture/deep-dive.md) | Control flow, the exported "API" surface, dependency/coupling graph, naming conventions & glossary, state/invariants, known sharp edges, and change guidance |
|
|
50
|
+
| [Operations](operations/monitoring.md) | Monitoring, alerting, SOP, support and debugging guidance for a library that has no runtime of its own |
|
|
51
|
+
| [ADRs](adrs/index.md) | Architecture decision records (currently none recorded) |
|
|
52
|
+
|
|
53
|
+
## Ownership
|
|
54
|
+
|
|
55
|
+
Team: platforms/plat-maise-surveyserv
|
|
56
|
+
|
|
57
|
+
System: platforms/surveyserv
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Alerts Configuration
|
|
2
|
+
|
|
3
|
+
No alerting is configured for this repo, and none is expected to be — it has no runtime to alert
|
|
4
|
+
on (see [Monitoring → Overview](monitoring.md)).
|
|
5
|
+
|
|
6
|
+
The only automated notification wired up in this repo is a best-effort Slack post on successful
|
|
7
|
+
npm publish, defined in `.github/workflows/automated-version-bump.yml` (`Notify Slack channel`
|
|
8
|
+
step): it posts to `SLACK_WEBHOOK_URL` (if configured as a repo secret) announcing the package
|
|
9
|
+
name, version, and release URL after a version-bump PR is merged and published. This step runs
|
|
10
|
+
with `continue-on-error: true` — a missing webhook or a failed post does not fail the workflow and
|
|
11
|
+
nobody is alerted if it silently fails.
|
|
12
|
+
|
|
13
|
+
If GitHub Actions failure notifications matter to this team (e.g. a failed `publish-after-merge`
|
|
14
|
+
job), they come from GitHub's own workflow-failure notification settings for the repo/org, not from
|
|
15
|
+
anything configured inside this codebase.
|
|
16
|
+
|
|
17
|
+
> [UNVERIFIED] Whether org-level GitHub Actions failure notifications (email/Slack) are configured
|
|
18
|
+
> for this repo outside of the workflow file itself was not checked — that would live in GitHub
|
|
19
|
+
> org/repo notification settings, not in source control.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# How to Debug
|
|
2
|
+
|
|
3
|
+
## 1. Enable the library's built-in debug logging
|
|
4
|
+
|
|
5
|
+
`src/utils.tsx`'s `appLogger` gates every internal `console.log` call behind a `localStorage` flag
|
|
6
|
+
read by `getFreshSurveyDebug` (`src/constants.ts`: `FRESH_SURVEY_DEBUG = 'fsc:debug'`). In the
|
|
7
|
+
browser console, on the page rendering the survey:
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
localStorage.setItem('fsc:debug', JSON.stringify({ log: true }));
|
|
11
|
+
// then reload the page
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
This turns on `appLogger('Survey', {...})`, `appLogger('Card', {...})`,
|
|
15
|
+
`appLogger('Standard', {...})`, `appLogger('Widget', {...})`, and per-`useEffect` logging inside
|
|
16
|
+
`useCardSurvey`/`useStandardSurvey` — each call logs the component name and the full current
|
|
17
|
+
props/state (survey, answers, active block/index, rendered blocks). This is the fastest way to see
|
|
18
|
+
exactly what the navigation engine believes the current state is.
|
|
19
|
+
|
|
20
|
+
`src/types.ts`'s `FreshSurveyDebugType` defines sibling debug flags used by other SurveyServ
|
|
21
|
+
frontends under the same `localStorage` mechanism (`fwa:debug` for the admin app, `mfe:debug` for
|
|
22
|
+
the microfrontend, `spw:debug` for the public widget, `spp:debug` for the public page, `fui:debug`
|
|
23
|
+
for `freemium-ui`) — if you're debugging an issue that spans this library and its host, check
|
|
24
|
+
whether the host has its own equivalent flag.
|
|
25
|
+
|
|
26
|
+
> **Security note:** this logs the *full* current props/state, including `answers.formValues` —
|
|
27
|
+
> real survey respondent data (which may include names, emails, free-text answers, or other PII).
|
|
28
|
+
> Only enable `fsc:debug` in non-production environments. If you must reproduce against production
|
|
29
|
+
> data, redact identifying fields before sharing the console output, and clear the `fsc:debug` key
|
|
30
|
+
> from `localStorage` afterward (it persists across page reloads and sessions until removed).
|
|
31
|
+
|
|
32
|
+
## 2. Reproduce in Storybook first
|
|
33
|
+
|
|
34
|
+
Before debugging inside a consumer app, try to reproduce with the same survey shape in Storybook
|
|
35
|
+
(`npm run storybook`) using a story under `src/stories/` or `src/survey/*.stories.tsx`, adapting
|
|
36
|
+
`src/mocks/index.ts`'s `MOCK_SURVEY` to match the reported shape. If it reproduces in Storybook,
|
|
37
|
+
the bug is in this repo; if it doesn't, the bug is likely in the consumer's integration (see
|
|
38
|
+
[Support](support.md)).
|
|
39
|
+
|
|
40
|
+
## 3. Isolate: navigation/branching vs. rendering vs. validation
|
|
41
|
+
|
|
42
|
+
- **Wrong question shown next / survey ends too early or too late** → the bug is almost certainly
|
|
43
|
+
in `src/survey/utils/logics.ts` (`getNextLogicalBlock`, `fetchNextBlockIndex`,
|
|
44
|
+
`getNextBlockIndex`, `evaluateConditions`). Log the survey's `blocks`/`dependent_blocks`/
|
|
45
|
+
`display_logic` and the current `answers.formValues` (via `fsc:debug`) and trace which branch of
|
|
46
|
+
`evaluateConditions` should fire for the given operator/answer.
|
|
47
|
+
- **A question renders with the wrong widget or missing data** → check the `switch` in
|
|
48
|
+
`src/survey/question.tsx` for that `question_type`, and the corresponding component under
|
|
49
|
+
`src/components/`.
|
|
50
|
+
- **A "Next"/"Submit"/"Skip" button is missing, disabled, or in the wrong state** → check
|
|
51
|
+
`canDisplayButton`/`canDisableButton` (`logics.ts`) for that `ActionButtonTypes` case — these
|
|
52
|
+
functions are shared across all three layouts, so also check whether `surveyStyle`/`isWidget`/
|
|
53
|
+
`isMobile` branches inside the specific case are gating on the wrong condition for your layout.
|
|
54
|
+
- **A field won't let the user proceed / shows a validation error incorrectly** → check the
|
|
55
|
+
relevant `*Validator` function in `logics.ts` (`dateValidator`, `formValidator`,
|
|
56
|
+
`textboxValidator`, `contactformValidator`, `fileUploadQnValidator`) and the corresponding
|
|
57
|
+
`ValidatingQuestions` error bucket.
|
|
58
|
+
|
|
59
|
+
## 4. Check for the known sharp edges first
|
|
60
|
+
|
|
61
|
+
Before deep-diving, rule out the documented gotchas in
|
|
62
|
+
[Deep Dive → Failure modes & sharp edges](../architecture/deep-dive.md#6-failure-modes--sharp-edges) —
|
|
63
|
+
in particular: `null` vs `undefined` in `answers.formValues` (skip vs. unanswered), the
|
|
64
|
+
`survey.updated_at`-keyed caching in `useSurvey`, and README examples that no longer match the
|
|
65
|
+
current prop shapes.
|
|
66
|
+
|
|
67
|
+
## 5. There is no server-side log to check
|
|
68
|
+
|
|
69
|
+
Because this library never makes a network call itself, there is no backend log, trace, or APM
|
|
70
|
+
entry to correlate against — everything observable happens in the consumer's browser. If the
|
|
71
|
+
report includes a network-layer symptom (e.g. `onFileUpload` failing), the failure is inside the
|
|
72
|
+
callback the **host** implemented, not inside this library's call site — confirm the host's
|
|
73
|
+
implementation of `onFileUpload`/`onSubmit` before assuming a library bug.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Monitoring — Overview
|
|
2
|
+
|
|
3
|
+
`freemium-survey-components` is a build-time-consumed UI component library, not a running
|
|
4
|
+
service. There is no deployed process, endpoint, container, or pod for this repo to monitor — it
|
|
5
|
+
has no uptime, no request rate, no dashboards of its own.
|
|
6
|
+
|
|
7
|
+
What actually needs watching lives elsewhere:
|
|
8
|
+
|
|
9
|
+
- **Runtime errors and rendering issues** surface inside the four consuming applications
|
|
10
|
+
(`survey-public-widget`, `survey-public-page`, `freshsurvey-web-admin-app`,
|
|
11
|
+
`survey-admin-microfrontend`) — their own frontend error monitoring / RUM tooling is where a bug
|
|
12
|
+
in this library would actually be observed in production. See
|
|
13
|
+
[How to Debug](how-to-debug.md) for how to correlate an issue back to this repo.
|
|
14
|
+
- **Build/publish health** is observable via GitHub Actions runs on
|
|
15
|
+
`.github/workflows/automated-version-bump.yml` (version-bump PR creation and the
|
|
16
|
+
publish-after-merge job) — a failed `npm publish` or `npm run build` step there is the closest
|
|
17
|
+
thing this repo has to an "incident."
|
|
18
|
+
- **Bundle size** (`lib/index.esm.js` / `lib/index.cjs.js` ~839 KB gzipped JS, `lib/bundle.css`
|
|
19
|
+
~42 KB gzipped as of this writing) is not tracked automatically in CI today — see
|
|
20
|
+
[Deep Dive → Failure modes](../architecture/deep-dive.md#6-failure-modes--sharp-edges). If this
|
|
21
|
+
is added in the future, this page should be updated to describe it.
|
|
22
|
+
- **Storybook/Chromatic** (`npm run chromatic`) provides visual-regression signal at PR time, not
|
|
23
|
+
runtime monitoring — see [Getting Started](../getting-started.md).
|
|
24
|
+
|
|
25
|
+
There is no APM, log aggregation, or metrics pipeline configured in this repo, and none is
|
|
26
|
+
expected for a library with this shape.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Recent Issues
|
|
2
|
+
|
|
3
|
+
This page tracks recent notable fixes merged to this repo, as a quick "have we seen this before"
|
|
4
|
+
reference. Source: `git log` on `main`. Update this list as new notable fixes land — do not let it
|
|
5
|
+
grow unbounded; keep roughly the last 10–15 entries and drop the oldest.
|
|
6
|
+
|
|
7
|
+
| Ticket | Summary |
|
|
8
|
+
| ------------------------ | --------------------------------------------------------------------------------------------------- |
|
|
9
|
+
| FSURVEY-8673 | Added `enableAnalytics` flag to `ProductConfigType`. |
|
|
10
|
+
| FSURVEY-8628 | Renamed the thank-you header CSS class to `fsc-thankyou-footer`. |
|
|
11
|
+
| FSURVEY-8568 | Fixed the `RESOLUTION` question and thank-you card rendering together and ignoring other questions. |
|
|
12
|
+
| FSURVEY-8425 | Fixed Facebook-channel-preview button alignment and a NUMBER-variant issue with a minimum of 0. |
|
|
13
|
+
| FSURVEY-8425 (follow-up) | Fixed MCQ rendering issues in the survey. |
|
|
14
|
+
| FSURVEY-8546 | Hid the widget footer when it would render no visible content. |
|
|
15
|
+
| MISC | Onboarded this repo to the Prism software catalog (`catalog-info.yaml`, `techdocs-ref: dir:.`). |
|
|
16
|
+
|
|
17
|
+
For anything not covered here, search closed PRs/issues on `freshdesk/freemium-survey-components`
|
|
18
|
+
by `FSURVEY-` ticket prefix, or search Confluence space `SP` for
|
|
19
|
+
"freemium-survey-components" (engineering briefs, roadmap docs) — see
|
|
20
|
+
[Deep Dive → Failure modes & sharp edges](../architecture/deep-dive.md#6-failure-modes--sharp-edges)
|
|
21
|
+
for the known, still-open structural issues (no test coverage, no PR CI gate, undeclared `lodash`
|
|
22
|
+
dependency, inconsistent HTML sanitization) that are more "known risk" than "recent issue."
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# SOP (Standard Operating Procedures)
|
|
2
|
+
|
|
3
|
+
There is no running service to operate, restart, scale, or fail over — see
|
|
4
|
+
[Monitoring → Overview](monitoring.md). The procedures below cover the two situations that
|
|
5
|
+
actually recur for a published component library: cutting a release, and reacting to a broken
|
|
6
|
+
release.
|
|
7
|
+
|
|
8
|
+
## Cutting a release
|
|
9
|
+
|
|
10
|
+
Follow [Getting Started → Versioning & npm publish flow](../getting-started.md#versioning--npm-publish-flow):
|
|
11
|
+
trigger `automated-version-bump.yml` via `workflow_dispatch`, choose the version bump type, review
|
|
12
|
+
and merge the generated PR, then confirm the `publish-after-merge` job succeeded (npm publish + git
|
|
13
|
+
tag + GitHub Release).
|
|
14
|
+
|
|
15
|
+
Before triggering a release:
|
|
16
|
+
|
|
17
|
+
- Run `npm run build` locally and confirm it succeeds (CI does not run this for you on the
|
|
18
|
+
version-bump PR — see [Deep Dive → Failure modes](../architecture/deep-dive.md#6-failure-modes--sharp-edges)).
|
|
19
|
+
- Run `npm run test-storybook` against a running/built Storybook and confirm no unexpected
|
|
20
|
+
snapshot diffs.
|
|
21
|
+
- If the change affects a `PUBLIC` export's shape (see
|
|
22
|
+
[Deep Dive → Contracts & interfaces](../architecture/deep-dive.md#2-contracts--interfaces)),
|
|
23
|
+
choose `minor` or `major` accordingly, not `patch`, and note the breaking change in the PR.
|
|
24
|
+
|
|
25
|
+
## Reacting to a broken release (bad version published to npm)
|
|
26
|
+
|
|
27
|
+
1. **Do not attempt to unpublish** — npm restricts unpublishing versions with existing downloads;
|
|
28
|
+
treat forward-fixing as the default path.
|
|
29
|
+
2. Trigger `automated-version-bump.yml` again with `version_type: patch` for a fix once the
|
|
30
|
+
regression is corrected on `main`.
|
|
31
|
+
3. Notify the four consuming repos (`survey-public-widget`, `survey-public-page`,
|
|
32
|
+
`freshsurvey-web-admin-app`, `survey-admin-microfrontend`) if any of them auto-upgrade or are
|
|
33
|
+
about to release against the bad version — see
|
|
34
|
+
[Deep Dive → Dependencies & coupling](../architecture/deep-dive.md#3-dependencies--coupling)
|
|
35
|
+
for why version skew across consumers means impact isn't uniform or immediate.
|
|
36
|
+
4. If the break is severe enough that consumers must pin away from it, communicate the last-known-good
|
|
37
|
+
version explicitly (e.g. in the incident thread / PR) since there is no automated deprecation
|
|
38
|
+
tooling wired into this repo's publish workflow.
|
|
39
|
+
|
|
40
|
+
## Rolling back a merged-but-bad PR on `main`
|
|
41
|
+
|
|
42
|
+
Standard git revert-and-release: revert the offending commit(s) on `main`, then follow "Cutting a
|
|
43
|
+
release" above for a `patch` release containing the revert. There is no separate rollback
|
|
44
|
+
mechanism for a published npm package beyond publishing a new (higher) version.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Support — Overview
|
|
2
|
+
|
|
3
|
+
**Owning team:** `platforms/plat-maise-surveyserv` (system: `platforms/surveyserv`).
|
|
4
|
+
|
|
5
|
+
Because this library has no runtime of its own, "support" for it means one of two things:
|
|
6
|
+
|
|
7
|
+
1. **A consumer app hits a bug that traces back to this library** (rendering, navigation/branching,
|
|
8
|
+
validation, or a question-type widget behaving incorrectly). Start with
|
|
9
|
+
[How to Debug](how-to-debug.md) to confirm the bug is actually in this repo and not in how a
|
|
10
|
+
consumer is calling `Survey`/`WebInAppSurvey`/`ChannelPreview`, then file/fix it here.
|
|
11
|
+
2. **A consumer needs a change to the public contract** (new question type, new prop, new exported
|
|
12
|
+
component) — see [Deep Dive → Change guidance](../architecture/deep-dive.md#7-change-guidance)
|
|
13
|
+
for what has to change together, and [Deep Dive → Contracts & interfaces](../architecture/deep-dive.md#2-contracts--interfaces)
|
|
14
|
+
for what's safe to change without a version bump vs. what's a breaking change for the four
|
|
15
|
+
dependent repos.
|
|
16
|
+
|
|
17
|
+
## Where to raise things
|
|
18
|
+
|
|
19
|
+
- **Bugs / feature requests against this repo:** GitHub issues on
|
|
20
|
+
`freshdesk/freemium-survey-components` (see `bugs.url` in `package.json`), or a PR directly using
|
|
21
|
+
this repo's `.github/pull_request_template.md`.
|
|
22
|
+
- **"Is this library or my integration?"** — reproduce against a Storybook story first
|
|
23
|
+
(`npm run storybook`); if the same survey/question renders correctly in Storybook with the same
|
|
24
|
+
props but not in your app, the issue is very likely in how the consumer is calling the component
|
|
25
|
+
(prop shape, missing `onSubmit`/`callback()` invocation, CSS variable collision — see
|
|
26
|
+
[Deep Dive → Dependencies & coupling → Hidden coupling](../architecture/deep-dive.md#3-dependencies--coupling)),
|
|
27
|
+
not in this library.
|
|
28
|
+
- **Freshrelease tracking:** ticket IDs referenced in commit/PR titles in this repo follow the
|
|
29
|
+
`FSURVEY-xxxx` pattern (see recent `git log` history and `.github/pull_request_template.md`'s
|
|
30
|
+
Ticket Reference section).
|
|
31
|
+
|
|
32
|
+
## What good bug reports include
|
|
33
|
+
|
|
34
|
+
- The exact `SurveyType` (or a minimal reproducing subset) and `answers` payload passed in.
|
|
35
|
+
- Which top-level component was used (`Survey` vs `WebInAppSurvey` vs `ChannelPreview`) and
|
|
36
|
+
`surveyStyle`/`surveyType`.
|
|
37
|
+
- Console output with `fsc:debug` logging enabled — see [How to Debug](how-to-debug.md).
|
|
38
|
+
- The installed `freemium-survey-components` version (`npm ls freemium-survey-components` in the
|
|
39
|
+
consumer repo).
|
package/lib/bundle.css
CHANGED
|
@@ -3283,7 +3283,7 @@ div.surveyserv-widget-container.compact-container-style .fsc-radio-group button.
|
|
|
3283
3283
|
.surveyserv-widget-container .question-container.thankyou .text,
|
|
3284
3284
|
.freemium-survey-components .question-container.thankyou .text {
|
|
3285
3285
|
text-align: center;
|
|
3286
|
-
white-space:
|
|
3286
|
+
white-space: normal;
|
|
3287
3287
|
font-weight: 500;
|
|
3288
3288
|
font-size: var(--message-font-size, var(--fsc-font-size));
|
|
3289
3289
|
word-break: break-word;
|
package/mkdocs.yml
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
site_name: 'Freemium Survey Components'
|
|
2
|
+
site_description: 'Functionality and Architecture Documentation for Freemium Survey Components'
|
|
3
|
+
|
|
4
|
+
nav:
|
|
5
|
+
- Home: index.md
|
|
6
|
+
- Getting Started: getting-started.md
|
|
7
|
+
- Architecture:
|
|
8
|
+
- Code Structure: architecture/code-structure.md
|
|
9
|
+
- Deep Dive: architecture/deep-dive.md
|
|
10
|
+
- Operations:
|
|
11
|
+
- Monitoring:
|
|
12
|
+
- Overview: operations/monitoring.md
|
|
13
|
+
- Alerts configuration: operations/alerts-configuration.md
|
|
14
|
+
- SOP: operations/sop.md
|
|
15
|
+
- Support:
|
|
16
|
+
- Overview: operations/support.md
|
|
17
|
+
- How to Debug: operations/how-to-debug.md
|
|
18
|
+
- Recent Issues: operations/recent-issues.md
|
|
19
|
+
- ADRs:
|
|
20
|
+
- Overview: adrs/index.md
|
|
21
|
+
|
|
22
|
+
plugins:
|
|
23
|
+
- techdocs-core
|
|
24
|
+
- mermaid2
|
|
25
|
+
|
|
26
|
+
markdown_extensions:
|
|
27
|
+
- admonition
|
|
28
|
+
- pymdownx.details
|
|
29
|
+
- pymdownx.superfences:
|
|
30
|
+
custom_fences:
|
|
31
|
+
- name: mermaid
|
|
32
|
+
class: mermaid
|
|
33
|
+
format: !!python/name:pymdownx.superfences.fence_code_format
|
|
34
|
+
- toc:
|
|
35
|
+
permalink: true
|
|
36
|
+
|
|
37
|
+
extra:
|
|
38
|
+
version: v1alpha1
|