Skip to content

Commit

Permalink
fix: Handle invalid colour codes on tags, allow default colours (#4822)
Browse files Browse the repository at this point in the history
  • Loading branch information
rolodato authored Nov 19, 2024
1 parent 051cc6f commit a33633f
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 41 deletions.
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

0 comments on commit a33633f

Please sign in to comment.