Skip to content

Annotations: FE#6481

Open
apata wants to merge 9 commits into
masterfrom
annotations/fe
Open

Annotations: FE#6481
apata wants to merge 9 commits into
masterfrom
annotations/fe

Conversation

@apata

@apata apata commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Changes

Adds the rest of Annotations feature. Depends on the BE PR.

Tests

  • Automated tests have been added

Changelog

  • Entry has been added to changelog

Documentation

Dark mode

  • The UI has been tested both in dark and light mode

TODO after merge

@apata apata added the preview label Jul 2, 2026
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Preview environment👷🏼‍♀️🏗️
PR-6481

@apata apata changed the title Add Annotations feature Annotations: FE Jul 3, 2026
Base automatically changed from annotations/be to master July 8, 2026 14:33
@apata apata mentioned this pull request Jul 8, 2026
4 tasks
apata and others added 9 commits July 8, 2026 19:51
…attern

- Show the site option as a disabled radio with a plan pill and hover tooltip instead of an inline warning message
- Add a reusable UpgradePill with diamond icon in both React and LiveView and use it in the annotation, segment, and goal settings modals plus the feature setup notice
@apata apata force-pushed the annotations/fe branch from 35e0106 to e81e9a5 Compare July 8, 2026 18:41
</div>
)

export const AnnotationAttributionLine = ({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit: the word "attribution" threw me off a bit when first reading it. How about "signature" instead?

return formatDay(date)
}

const NOTE_RECOMMENDED_MAX_LENGTH = 250

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the word "recommended"? Same question about the UI label

Suggested change
const NOTE_RECOMMENDED_MAX_LENGTH = 250
const NOTE_MAX_LENGTH = 250

Comment on lines +84 to +85
const defaultNote = ''
const [note, setNote] = useState(defaultNote)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it look like the default value is needed elsewhere. Let's inline?

Suggested change
const defaultNote = ''
const [note, setNote] = useState(defaultNote)
const [note, setNote] = useState('')

Comment on lines +129 to +131
const saveableNote = trimmedNote.length
? trimmedNote
: notePlaceholder

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we prohibit creating an empty note rather than saving the note with notePlaceholder? When you open the "Add note" modal and hit save directly, it creates this note:

Image

notePlaceholder: string
}

export const CreateAnnotationModal = ({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think notePlaceHolder and initialType shouldn't be props, but rather fixed values defined as module level consts. They do make sense as props in the "Edit note" modal, but not in the "Add note" one.

}: AnnotationModalProps &
ApiRequestProps & {
onSave: (input: Pick<Annotation, 'id' | 'note' | 'type'>) => void
onDelete?: (annotation: Annotation) => void

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this function is always provided, so we could enforce this prop and remove the typeof onDelete === 'function' check below?

)

it.each([[AnnotationType.personal], [AnnotationType.site]])(
'forbids the public role from adding %s annotations',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we test the same for super admins?

Role.owner
]

export function canEditAnnotation({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole function body is very similar to canAddAnnotation. Maybe extract a private function like hasPermissions({type, user, siteAnnotationsAvailable}) to not repeat this logic?

Comment on lines +136 to +140
if (tooltip.persistent && isTouchDevice === false) {
document.addEventListener('click', onClickOutside)
} else {
document.removeEventListener('click', onClickOutside)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The else branch should be unnecessary because the return function always cleans the eventListener up.

{!!annotationDatetime && canAddAnnotation && (
<Button
size="xs"
className="flex-1 bg-gray-600/70 border-gray-600/70 hover:bg-gray-600 hover:border-gray-600"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The classname between the two buttons should be exactly the same so perhaps let's extract that into a variable?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants