Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Handle invalid colour codes on tags, allow default colours #4822

Merged
merged 10 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions api/projects/tags/migrations/0007_alter_tag_color.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 4.2.16 on 2024-11-08 18:14

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("tags", "0006_alter_tag_type"),
]

operations = [
migrations.AlterField(
model_name="tag",
name="color",
field=models.CharField(
default="#6837FC",
help_text="Hexadecimal value of the tag color",
max_length=10,
),
),
]
2 changes: 1 addition & 1 deletion api/projects/tags/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class TagType(models.Choices):
class Tag(AbstractBaseExportableModel):
label = models.CharField(max_length=100)
color = models.CharField(
max_length=10, help_text="Hexadecimal value of the tag color"
max_length=10, help_text="Hexadecimal value of the tag color", default="#6837FC"
)
description = models.CharField(max_length=512, blank=True, null=True)
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="tags")
Expand Down
1 change: 1 addition & 0 deletions frontend/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ export default {
githubIssue: 'GitHub Issue',
githubPR: 'Github PR',
},
defaultTagColor: '#3d4db6',
isCustomFlagsmithUrl:
Project.flagsmithClientAPI !== 'https://edge.api.flagsmith.com/api/v1/',
modals: {
Expand Down
15 changes: 14 additions & 1 deletion frontend/common/utils/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import WarningMessage from 'components/WarningMessage'
import Constants from 'common/constants'
import Format from './format'
import { defaultFlags } from 'common/stores/default-flags'
import Color from 'color'

const semver = require('semver')

Expand Down Expand Up @@ -110,11 +111,23 @@ const Utils = Object.assign({}, require('./base/_utils'), {
return typeof value === 'number'
},

colour(
c: string,
fallback = Constants.defaultTagColor,
): InstanceType<typeof Color> {
let res: Color
try {
res = Color(c)
} catch (_) {
res = Color(fallback)
}
return res
},

copyFeatureName: (featureName: string) => {
navigator.clipboard.writeText(featureName)
toast('Copied to clipboard')
},

displayLimitAlert(type: string, percentage: number | undefined) {
const envOrProject =
type === 'segment overrides' ? 'environment' : 'project'
Expand Down
6 changes: 3 additions & 3 deletions frontend/web/components/FeatureAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,12 @@ export const FeatureAction: FC<FeatureActionProps> = ({
protectedTags?.length > 1 ? 's' : ''
} ${protectedTags
?.map((tag) => {
const tagColor = getTagColor(tag)
const tagColor = Utils.colour(getTagColor(tag))
return `<strong class='chip chip--xs d-inline-block ms-1' style='background:${color(
tagColor,
).fade(0.92)};border-color:${color(tagColor).darken(
).fade(0.92)};border-color:${tagColor.darken(
0.1,
)};color:${color(tagColor).darken(0.1)};'>
)};color:${tagColor.darken(0.1)};'>
${tag.label}
</strong>`
})
Expand Down
15 changes: 7 additions & 8 deletions frontend/web/components/ToggleChip.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import React from 'react'
import cx from 'classnames'
import color from 'color'
import Icon from './Icon'
import Utils from 'common/utils/utils'

export default function (props) {
const colour = Utils.colour(props.color)
return (
<Row
style={
props.color
? {
backgroundColor: props.children
? color(props.color).fade(0.92)
: color(props.color).fade(0.76),
border: `1px solid ${color(props.color).fade(0.76)}`,
color: color(props.color).darken(0.1),
? colour.fade(0.92)
: colour.fade(0.76),
border: `1px solid ${colour.fade(0.76)}`,
color: colour.darken(0.1),
}
: null
}
Expand All @@ -26,9 +27,7 @@ export default function (props) {
border:
props.active || !props.children
? 'none'
: `1px solid ${color(props.color ? props.color : '#6837FC').fade(
0.76,
)}`,
: `1px solid ${colour.fade(0.76)}`,
marginRight: props.children ? '0.5rem' : '0',
}}
className='icon-check'
Expand Down
11 changes: 5 additions & 6 deletions frontend/web/components/tags/Tag.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { FC } from 'react'
import color from 'color'
import cx from 'classnames'

import { Tag as TTag } from 'common/types/responses'
Expand Down Expand Up @@ -34,13 +33,13 @@ const Tag: FC<TagType> = ({
selected,
tag,
}) => {
const tagColor = getTagColor(tag, selected)
const tagColor = Utils.colour(getTagColor(tag, selected))
if (isDot) {
return (
<div
className={'tag--dot'}
style={{
backgroundColor: `${color(tagColor).darken(0.1)}`,
backgroundColor: `${tagColor.darken(0.1)}`,
}}
/>
)
Expand Down Expand Up @@ -72,9 +71,9 @@ const Tag: FC<TagType> = ({
}
}}
style={{
backgroundColor: `${color(tagColor).fade(0.92)}`,
border: `1px solid ${color(tagColor).fade(0.76)}`,
color: `${color(tagColor).darken(0.1)}`,
backgroundColor: `${tagColor.fade(0.92)}`,
border: `1px solid ${tagColor.fade(0.76)}`,
color: `${tagColor.darken(0.1)}`,
}}
className={cx('chip', className)}
>
Expand Down
31 changes: 9 additions & 22 deletions frontend/web/components/tags/TagContent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { FC } from 'react'
import { Tag as TTag } from 'common/types/responses'
import color from 'color'
import Format from 'common/utils/format'
import { IonIcon } from '@ionic/react'
import { alarmOutline, lockClosed } from 'ionicons/icons'
Expand All @@ -10,6 +9,7 @@ import OrganisationStore from 'common/stores/organisation-store'
import Utils from 'common/utils/utils'
import classNames from 'classnames'
import Icon from 'components/Icon'
import Color from 'color'
type TagContent = {
tag: Partial<TTag>
}
Expand All @@ -26,15 +26,10 @@ const renderIcon = (
tagLabel: string,
isPermanent: boolean,
) => {
const darkened = tagColor.darken(0.1).string()
switch (tagType) {
case 'STALE':
return (
<IonIcon
className='ms-1'
icon={alarmOutline}
color={color(tagColor).darken(0.1).string()}
/>
)
return <IonIcon className='ms-1' icon={alarmOutline} color={darkened} />
case 'GITHUB':
switch (tagLabel) {
case 'PR Open':
Expand All @@ -53,13 +48,7 @@ const renderIcon = (
return
}
default:
return isPermanent ? (
<IonIcon
className='ms-1'
icon={lockClosed}
color={color(tagColor).darken(0.1).string()}
/>
) : null
return isPermanent ? <IonIcon className='ms-1' icon={lockClosed} color={darkened} /> : null
}
}

Expand Down Expand Up @@ -90,16 +79,14 @@ const getTooltip = (tag: TTag | undefined) => {
tooltip =
'Features marked with this tag are not monitored for staleness and have deletion protection.'
}
const tagColor = getTagColor(tag, false)
const tagColor = Utils.colour(getTagColor(tag, false))

if (isTruncated) {
return `<div>
<span
style='background-color: ${color(tagColor).fade(
0.92,
)}; border: 1px solid ${color(tagColor).fade(0.76)}; color: ${color(
tagColor,
).darken(0.1)};'
style='background-color: ${tagColor.fade(0.92)};
border: 1px solid ${tagColor.fade(0.76)};
color: ${tagColor.darken(0.1)};'
class="chip d-inline-block chip--xs me-1${
disabled ? ' disabled' : ''
}"
Expand Down Expand Up @@ -130,7 +117,7 @@ const TagContent: FC<TagContent> = ({ tag }) => {
})}
>
{tagLabel}
{renderIcon(tag.type!, tag.color!, tag.label!, tag.is_permanent)}
{renderIcon(tag.type!, Utils.colour(tag.color), tag.label!)}
</span>
}
>
Expand Down
Loading