-
Notifications
You must be signed in to change notification settings - Fork 17
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
Support dimensions filter or,and,not group #128
Support dimensions filter or,and,not group #128
Conversation
Walkthrough이 변경 사항은 Changes
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (1)
src/QueryEditorGA4.tsx (1)
324-329
: 'onDelete' 속성은 생략이 가능할 수 있습니다.'GAFilterExpressionComponent'에 'onDelete={undefined}'를 명시적으로 전달하고 있습니다. 만약 'onDelete' 속성이 선택적(optional)인 경우, 해당 속성을 생략해도 무방합니다.
<GAFilterExpressionComponent expression={query.dimensionFilter} onChange={this.onFiltersExpressionChange} selectedDimensions={selectedDimensions} - onDelete={undefined} />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (3)
- src/Filter.tsx (1 hunks)
- src/QueryEditorGA4.tsx (3 hunks)
- src/types.ts (1 hunks)
🧰 Additional context used
🪛 Biome
src/Filter.tsx
[error] 129-129: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 133-133: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
🔇 Additional comments (4)
src/types.ts (1)
101-103
:filterType
속성 변경 확인 필요
GAFilter
인터페이스의filterType
속성이GADimensionFilterType
으로 변경된 것은 PR의 목적에 부합합니다.그러나 다음 사항들을 확인해 주시기 바랍니다:
GAMetricFilterType
의 제거가 의도적인지 확인해 주세요. 만약 메트릭 필터도 지원해야 한다면, 유니온 타입을 고려해 보시는 것이 좋겠습니다.undefined
를 제거함으로써filterType
이 필수 속성이 되었습니다. 이것이 의도한 바인지 확인해 주세요.다음 스크립트를 실행하여
GAMetricFilterType
의 사용 여부를 확인해 주세요:✅ Verification successful
이전 스크립트에서
typescript
파일 유형을 인식하지 못했습니다. 올바른 파일 유형을 사용하여 다시 시도하겠습니다.
filterType
속성 변경이 확인되었습니다
filterType
이GADimensionFilterType
으로 변경된 것이 PR의 목적에 부합하며,GAMetricFilterType
의 사용 여부를 확인한 결과 현재 코드베이스 내에서 사용되지 않는 것으로 보입니다. 필요하지 않다면GAMetricFilterType
의 정의도 제거하는 방안을 고려해보세요.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Check if GAMetricFilterType is still used in the codebase # Test: Search for GAMetricFilterType usage rg --type typescript 'GAMetricFilterType'Length of output: 76
Script:
#!/bin/bash # Description: Check if GAMetricFilterType is still used in the codebase # Test: Search for GAMetricFilterType usage using the correct TypeScript file type rg --type ts 'GAMetricFilterType'Length of output: 165
src/QueryEditorGA4.tsx (3)
17-17
: 타입 임포트가 올바르게 수정되었습니다.'GAFilterExpression' 타입을 추가하여 타입 정의를 업데이트하셨습니다. 적절한 수정입니다.
125-127
: onFiltersExpressionChange 함수의 인자 타입이 개선되었습니다.함수의 인자를 일반적인 'any' 타입에서 'GAFilterExpression' 타입으로 변경하여 타입 안전성을 향상시켰습니다. 좋은 수정입니다.
14-14
: 'Filter' 모듈의 임포트 경로를 확인해주세요.'Filter' 모듈이 프로젝트 내에서 올바르게 임포트되고 있는지 확인해주세요. 상대 경로 또는 절대 경로를 사용할 때 경로가 정확한지 확인해야 합니다.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Outside diff range and nitpick comments (2)
src/Filter.tsx (2)
6-20
: 컴포넌트 프롭스와 초기 설정이 개선되었습니다.컴포넌트의 이름 변경과 새로운 프롭스 추가는 기능 확장을 잘 반영하고 있습니다. 표현식 타입의 도입으로 더 복잡한 필터링이 가능해졌습니다.
onDelete
프롭의 타입을 더 명확하게 하기 위해 다음과 같이 변경하는 것을 고려해보세요:onDelete?: (() => void) | undefined;이렇게 하면
onDelete
가 함수이거나undefined
일 수 있다는 것을 더 명확히 표현할 수 있습니다.
50-109
: 표현식 렌더링 로직이 잘 구조화되어 있습니다.표현식 타입별로 분리된 렌더링 함수와 재귀적 접근 방식은 복잡한 중첩 표현식을 효과적으로 처리할 수 있게 해줍니다.
성능 최적화를 위해 다음 사항을 고려해보세요:
- 깊게 중첩된 구조의 경우 성능 문제가 발생할 수 있습니다.
React.memo
를 사용하여 불필요한 리렌더링을 방지할 수 있습니다:const GAFilterExpressionComponent: React.FC<Props> = React.memo(({ expression, onChange, onDelete, selectedDimensions }) => { // ... existing code ... });
- 큰 규모의 표현식 트리의 경우, 가상화 기법(예: react-window)을 사용하여 렌더링 성능을 개선할 수 있습니다.
이러한 최적화는 대규모 필터 표현식을 다룰 때 사용자 경험을 향상시킬 수 있습니다.
} as GAStringFilter | ||
targetData.stringFilter = stringFilter | ||
} | ||
} | ||
} | ||
data[index].filter! = targetData | ||
dimensionFilter.orGroup!.expressions = data | ||
onChange({ | ||
...query, dimensionFilter | ||
}) | ||
} | ||
|
||
// const fieldNameChange = (value: SelectableValue<string>, index: number) => { | ||
// let data = [...filterFields]; | ||
// const { query, onChange} = props; | ||
|
||
// data[index].filedName = value.value || '' | ||
// setFormFields(data); | ||
// | ||
// onChange({...query, metricFilter: data[index]}) | ||
// } | ||
|
||
onChange({ filter: newFilter }); | ||
}; | ||
|
||
const renderFilterContent = () => { | ||
switch (filter.filterType) { | ||
case GADimensionFilterType.STRING: | ||
return renderStringFilter(filter.stringFilter!); | ||
case GADimensionFilterType.IN_LIST: | ||
return renderInListFilter(filter.inListFilter!); | ||
default: | ||
return null; | ||
} | ||
}; | ||
|
||
return ( | ||
<VerticalGroup> | ||
<Select | ||
options={selectedDimensions} | ||
value={filter.fieldName} | ||
onChange={(option) => onChange({ filter: { ...filter, fieldName: option.value! } })} | ||
placeholder="Select field" | ||
/> | ||
<Select | ||
options={filterTypes} | ||
value={filter.filterType} | ||
onChange={handleFilterTypeChange} | ||
/> | ||
{renderFilterContent()} | ||
</VerticalGroup> | ||
); | ||
}; | ||
|
||
const renderStringFilter = (stringFilter: GAStringFilter) => { | ||
const matchTypes = Object.values(GAStringFilterMatchType).map(value => ({ label: value, value })); | ||
|
||
return ( | ||
<VerticalGroup> | ||
<Select | ||
options={matchTypes} | ||
value={stringFilter.matchType} | ||
onChange={(option) => onChange({ filter: { ...expression.filter!, stringFilter: { ...stringFilter, matchType: option.value! } } })} | ||
/> | ||
<Input | ||
value={stringFilter.value} | ||
onChange={(e) => onChange({ filter: { ...expression.filter!, stringFilter: { ...stringFilter, value: e.currentTarget.value } } })} | ||
placeholder="Value" | ||
/> | ||
<RadioButtonGroup | ||
options={[ | ||
{ label: 'Case sensitive', value: true }, | ||
{ label: 'Case insensitive', value: false }, | ||
]} | ||
value={stringFilter.caseSensitive} | ||
onChange={(value) => onChange({ filter: { ...expression.filter!, stringFilter: { ...stringFilter, caseSensitive: value } } })} | ||
/> | ||
</VerticalGroup> | ||
); | ||
}; | ||
|
||
const renderInListFilter = (inListFilter: GAInListFilter = { values: [], caseSensitive: false }) => { | ||
return ( | ||
<VerticalGroup> | ||
<Input | ||
value={inListFilter.values.join(', ')} | ||
onChange={(e) => onChange({ filter: { ...expression.filter!, inListFilter: { ...inListFilter, values: e.currentTarget.value.split(',').map(v => v.trim()) } } })} | ||
placeholder="Values (comma-separated)" | ||
/> | ||
<RadioButtonGroup | ||
options={[ | ||
{ label: 'Case sensitive', value: true }, | ||
{ label: 'Case insensitive', value: false }, | ||
]} | ||
value={inListFilter.caseSensitive} | ||
onChange={(value) => onChange({ filter: { ...expression.filter!, inListFilter: { ...inListFilter, caseSensitive: value } } })} | ||
/> | ||
</VerticalGroup> | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
필터 렌더링 로직 개선 제안
필터 타입별 렌더링 로직이 잘 구조화되어 있고, Grafana UI 컴포넌트를 일관되게 사용하고 있습니다. 하지만 코드 재사용성과 타입 안전성을 더욱 개선할 수 있습니다.
다음과 같은 개선 사항을 고려해보세요:
-
공통 로직을 별도의 함수로 추출하여 코드 중복을 줄입니다. 예를 들어,
RadioButtonGroup
을 렌더링하는 로직은 두 필터 타입에서 유사하게 사용됩니다. -
타입 가드를 사용하여 타입 안전성을 높입니다:
function isStringFilter(filter: GAFilter): filter is GAFilter & { stringFilter: GAStringFilter } {
return filter.filterType === GADimensionFilterType.STRING;
}
function isInListFilter(filter: GAFilter): filter is GAFilter & { inListFilter: GAInListFilter } {
return filter.filterType === GADimensionFilterType.IN_LIST;
}
// 사용 예:
if (isStringFilter(filter)) {
// filter.stringFilter는 여기서 타입 안전합니다.
}
- 필터 타입별 로직을 별도의 컴포넌트로 분리하여 코드의 모듈성을 높입니다.
이러한 변경으로 코드의 유지보수성과 확장성이 향상될 것입니다.
<> | ||
<HorizontalGroup> | ||
<Field label="dimension"> | ||
<Select | ||
options={props.query.selectedDimensions} | ||
onChange={(value, action) => { | ||
action.name = "dimensionChanged" | ||
fieldNameChange(value, action, index) | ||
}} | ||
value={filter?.fieldName} | ||
/> | ||
</Field> | ||
<Field label="filter type"> | ||
|
||
<Select | ||
options={dimensionFilterType} | ||
onChange={(value, action) => { | ||
action.name = "filterTypeChanged" | ||
fieldNameChange(value, action, index) | ||
}} | ||
value={filter?.filterType?.toString()} | ||
/> | ||
</Field> | ||
|
||
{ | ||
filter?.filterType === GADimensionFilterType.STRING && | ||
<> | ||
<Field label="match type"> | ||
<Select | ||
options={stringFilterMatchType} | ||
onChange={(value, action) => { | ||
action.name = "matchTypeChanged" | ||
fieldNameChange(value, action, index) | ||
}} | ||
value={filter.stringFilter?.matchType.toString()} | ||
/> | ||
</Field> | ||
|
||
</> | ||
} | ||
{ | ||
filter?.filterType === GADimensionFilterType.STRING && | ||
<> | ||
<Field label="value" invalid={filter.stringFilter?.value === ''} error={filter.stringFilter?.value === '' ? 'This input is required' : ''}> | ||
<Input required onChange={(e) => { | ||
filedValueChange(e.currentTarget.value, index) | ||
}} | ||
value={filter.stringFilter?.value} | ||
></Input> | ||
</Field> | ||
</> | ||
} | ||
{ | ||
filter?.filterType === GADimensionFilterType.IN_LIST && | ||
<> | ||
<Field label="values sperate by comma" invalid={filter.inListFilter?.values.join(',') === ''} error={filter.inListFilter?.values.join(',') === '' ? 'This input is required' : ''} > | ||
<Input required onChange={(e) => { | ||
filedValueChange(e.currentTarget.value, index) | ||
}} | ||
value={filter.inListFilter?.values.join(',')} | ||
></Input> | ||
</Field> | ||
</> | ||
} | ||
{/* <Field label="filter type" description="filter type"> */} | ||
<Button variant='secondary' icon='minus' onClick={() => removeFields(index)} ></Button> | ||
<Button variant='secondary' icon='plus' onClick={addFields} ></Button> | ||
|
||
{/* </Field> */} | ||
</HorizontalGroup> | ||
|
||
</> | ||
) | ||
})} | ||
{ | ||
(Object.keys(dimensionFilter).length === 0 || dimensionFilter.orGroup?.expressions.length === 0) && <Button variant='secondary' icon='plus' onClick={addFields} ></Button> | ||
} | ||
</VerticalGroup> | ||
{/* <Field label="filter type" description="filter type"> */} | ||
{/* </Field> */} | ||
</div> | ||
</> | ||
<FieldSet> | ||
<HorizontalGroup> | ||
<Select | ||
options={expressionTypes} | ||
value={Object.keys(expression).length === 0 ? 'none' : Object.keys(expression)[0]} | ||
onChange={handleExpressionTypeChange} | ||
/> | ||
{renderExpressionContent()} | ||
{onDelete && ( | ||
<Button variant="destructive" onClick={onDelete}> | ||
Delete | ||
</Button> | ||
)} | ||
</HorizontalGroup> | ||
</FieldSet> | ||
); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
메인 렌더 함수의 접근성 및 사용자 경험 개선 제안
메인 렌더 함수가 Grafana UI 컴포넌트를 적절히 사용하고 있으며, 삭제 버튼의 조건부 렌더링은 좋은 방식입니다.
접근성과 사용자 경험을 더욱 개선하기 위해 다음 사항을 고려해보세요:
aria-label
속성을 추가하여 스크린 리더 사용자를 위한 접근성을 향상시킵니다:
<Select
options={expressionTypes}
value={Object.keys(expression).length === 0 ? 'none' : Object.keys(expression)[0]}
onChange={handleExpressionTypeChange}
aria-label="필터 표현식 타입 선택"
/>
- 삭제 버튼에 확인 대화상자를 추가하여 실수로 인한 삭제를 방지합니다:
<Button
variant="destructive"
onClick={() => {
if (window.confirm('이 필터 표현식을 삭제하시겠습니까?')) {
onDelete();
}
}}
>
삭제
</Button>
- 툴팁을 추가하여 각 옵션에 대한 추가 정보를 제공합니다:
<Select
options={expressionTypes.map(option => ({
...option,
tooltip: `${option.label} 표현식을 생성합니다.`
}))}
// ... 기존 props
/>
이러한 개선사항들은 컴포넌트의 사용성과 접근성을 크게 향상시킬 것입니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Outside diff range and nitpick comments (1)
src/Filter.tsx (1)
7-12
: Props 인터페이스에 JSDoc 문서화 추가 필요
각 prop의 목적과 사용법을 명확히 하기 위해 JSDoc 문서화를 추가하는 것이 좋습니다.
다음과 같이 문서화를 추가해보세요:
+/**
+ * GAFilterExpressionComponent의 Props 인터페이스
+ * @interface Props
+ */
interface Props {
+ /** 현재 필터 표현식 */
expression: GAFilterExpression;
+ /** 필터 표현식이 변경될 때 호출되는 콜백 */
onChange: (expression: GAFilterExpression) => void;
+ /** 필터 삭제 시 호출되는 콜백 */
onDelete?: () => void;
+ /** 선택 가능한 차원 목록 */
selectedDimensions: Array<SelectableValue<string>>;
}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
- src/Filter.tsx (1 hunks)
🔇 Additional comments (2)
src/Filter.tsx (2)
120-139
: 필터 타입 변경 처리가 잘 구현되었습니다
이전 리뷰에서 지적된 delete
연산자 사용을 undefined
할당으로 수정하여 성능 및 가독성이 개선되었습니다.
217-234
: 메인 렌더 함수가 잘 구현되었습니다
컴포넌트의 구조가 명확하고, 조건부 렌더링이 적절하게 사용되었습니다.
const renderStringFilter = (stringFilter: GAStringFilter) => { | ||
const matchTypes = Object.values(GAStringFilterMatchType).map(value => ({ label: value, value })); | ||
|
||
return ( | ||
<VerticalGroup> | ||
<Select | ||
options={matchTypes} | ||
value={stringFilter.matchType} | ||
onChange={(option) => onChange({ filter: { ...expression.filter!, stringFilter: { ...stringFilter, matchType: option.value! } } })} | ||
/> | ||
<Input | ||
value={stringFilter.value} | ||
onChange={(e) => onChange({ filter: { ...expression.filter!, stringFilter: { ...stringFilter, value: e.currentTarget.value } } })} | ||
placeholder="Value" | ||
/> | ||
<RadioButtonGroup | ||
options={[ | ||
{ label: 'Case sensitive', value: true }, | ||
{ label: 'Case insensitive', value: false }, | ||
]} | ||
value={stringFilter.caseSensitive} | ||
onChange={(value) => onChange({ filter: { ...expression.filter!, stringFilter: { ...stringFilter, caseSensitive: value } } })} | ||
/> | ||
</VerticalGroup> | ||
); | ||
}; | ||
|
||
const renderInListFilter = (inListFilter: GAInListFilter = { values: [], caseSensitive: false }) => { | ||
return ( | ||
<VerticalGroup> | ||
<Input | ||
value={inListFilter.values.join(', ')} | ||
onChange={(e) => onChange({ filter: { ...expression.filter!, inListFilter: { ...inListFilter, values: e.currentTarget.value.split(',').map(v => v.trim()) } } })} | ||
placeholder="Values (comma-separated)" | ||
/> | ||
<RadioButtonGroup | ||
options={[ | ||
{ label: 'Case sensitive', value: true }, | ||
{ label: 'Case insensitive', value: false }, | ||
]} | ||
value={inListFilter.caseSensitive} | ||
onChange={(value) => onChange({ filter: { ...expression.filter!, inListFilter: { ...inListFilter, caseSensitive: value } } })} | ||
/> | ||
</VerticalGroup> | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
필터 렌더링 함수의 중복 코드 리팩토링 필요
renderStringFilter
와 renderInListFilter
함수에서 RadioButtonGroup
관련 코드가 중복되어 있습니다.
다음과 같이 공통 컴포넌트로 추출하는 것을 제안합니다:
const CaseSensitivityToggle: React.FC<{
value: boolean;
onChange: (value: boolean) => void;
}> = ({ value, onChange }) => (
<RadioButtonGroup
options={[
{ label: 'Case sensitive', value: true },
{ label: 'Case insensitive', value: false },
]}
value={value}
onChange={onChange}
/>
);
이를 각 필터 렌더링 함수에서 재사용할 수 있습니다.
const renderNotExpression = () => { | ||
return ( | ||
<GAFilterExpressionComponent | ||
expression={expression.notExpression!} | ||
selectedDimensions={selectedDimensions} | ||
onChange={(newExpr) => onChange({ notExpression: newExpr })} | ||
onDelete={() => onChange({})} | ||
/> | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
renderNotExpression 함수의 타입 안전성 개선 필요
expression.notExpression!
에서 non-null assertion을 사용하고 있어 런타임 오류가 발생할 수 있습니다.
다음과 같이 타입 체크를 추가하는 것이 좋습니다:
const renderNotExpression = () => {
+ if (!expression.notExpression) {
+ console.error('notExpression이 정의되지 않았습니다');
+ return null;
+ }
return (
<GAFilterExpressionComponent
- expression={expression.notExpression!}
+ expression={expression.notExpression}
selectedDimensions={selectedDimensions}
onChange={(newExpr) => onChange({ notExpression: newExpr })}
onDelete={() => onChange({})}
/>
);
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const renderNotExpression = () => { | |
return ( | |
<GAFilterExpressionComponent | |
expression={expression.notExpression!} | |
selectedDimensions={selectedDimensions} | |
onChange={(newExpr) => onChange({ notExpression: newExpr })} | |
onDelete={() => onChange({})} | |
/> | |
); | |
}; | |
const renderNotExpression = () => { | |
if (!expression.notExpression) { | |
console.error('notExpression이 정의되지 않았습니다'); | |
return null; | |
} | |
return ( | |
<GAFilterExpressionComponent | |
expression={expression.notExpression} | |
selectedDimensions={selectedDimensions} | |
onChange={(newExpr) => onChange({ notExpression: newExpr })} | |
onDelete={() => onChange({})} | |
/> | |
); | |
}; |
const handleExpressionTypeChange = (option: SelectableValue<string>) => { | ||
let newExpression: GAFilterExpression; | ||
switch (option.value) { | ||
case 'none': | ||
newExpression = {}; | ||
break; | ||
case 'andGroup': | ||
case 'orGroup': | ||
newExpression = { [option.value]: { expressions: [] } }; | ||
break; | ||
case 'notExpression': | ||
newExpression = { notExpression: {} }; | ||
break; | ||
case GADimensionFilterType.IN_LIST: | ||
if (targetData.inListFilter !== undefined) { | ||
targetData.inListFilter.values = value.split(',') | ||
data[index].filter = targetData | ||
} | ||
case 'filter': | ||
newExpression = { | ||
filter: { | ||
fieldName: '', | ||
filterType: GADimensionFilterType.STRING, | ||
stringFilter: { matchType: GAStringFilterMatchType.EXACT, value: '', caseSensitive: false } | ||
} | ||
}; | ||
break; | ||
default: | ||
return; | ||
} | ||
dimensionFilter.orGroup!.expressions = data | ||
onChange({ | ||
...query, dimensionFilter | ||
}) | ||
} | ||
const fieldNameChange = (value: SelectableValue<string>, action: ActionMeta, index: number) => { | ||
let data = [...dimensionFilter.orGroup!.expressions]; | ||
let targetData = data[index].filter! | ||
const { query, onChange } = props; | ||
if (value.value !== undefined) { | ||
switch (action.name) { | ||
case "dimensionChanged": | ||
targetData.fieldName = value.value | ||
onChange(newExpression); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handleExpressionTypeChange 함수의 오류 처리 개선 필요
switch 문의 default 케이스가 비어있어 예상치 못한 타입이 들어올 경우 적절한 처리가 되지 않습니다.
다음과 같이 개선해보세요:
const handleExpressionTypeChange = (option: SelectableValue<string>) => {
+ if (!option.value) {
+ console.error('옵션 값이 없습니다');
+ return;
+ }
let newExpression: GAFilterExpression;
switch (option.value) {
// ... existing cases ...
default:
- return;
+ console.error(`지원하지 않는 표현식 타입입니다: ${option.value}`);
+ return;
}
onChange(newExpression);
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const handleExpressionTypeChange = (option: SelectableValue<string>) => { | |
let newExpression: GAFilterExpression; | |
switch (option.value) { | |
case 'none': | |
newExpression = {}; | |
break; | |
case 'andGroup': | |
case 'orGroup': | |
newExpression = { [option.value]: { expressions: [] } }; | |
break; | |
case 'notExpression': | |
newExpression = { notExpression: {} }; | |
break; | |
case GADimensionFilterType.IN_LIST: | |
if (targetData.inListFilter !== undefined) { | |
targetData.inListFilter.values = value.split(',') | |
data[index].filter = targetData | |
} | |
case 'filter': | |
newExpression = { | |
filter: { | |
fieldName: '', | |
filterType: GADimensionFilterType.STRING, | |
stringFilter: { matchType: GAStringFilterMatchType.EXACT, value: '', caseSensitive: false } | |
} | |
}; | |
break; | |
default: | |
return; | |
} | |
dimensionFilter.orGroup!.expressions = data | |
onChange({ | |
...query, dimensionFilter | |
}) | |
} | |
const fieldNameChange = (value: SelectableValue<string>, action: ActionMeta, index: number) => { | |
let data = [...dimensionFilter.orGroup!.expressions]; | |
let targetData = data[index].filter! | |
const { query, onChange } = props; | |
if (value.value !== undefined) { | |
switch (action.name) { | |
case "dimensionChanged": | |
targetData.fieldName = value.value | |
onChange(newExpression); | |
}; | |
const handleExpressionTypeChange = (option: SelectableValue<string>) => { | |
if (!option.value) { | |
console.error('옵션 값이 없습니다'); | |
return; | |
} | |
let newExpression: GAFilterExpression; | |
switch (option.value) { | |
case 'none': | |
newExpression = {}; | |
break; | |
case 'andGroup': | |
case 'orGroup': | |
newExpression = { [option.value]: { expressions: [] } }; | |
break; | |
case 'notExpression': | |
newExpression = { notExpression: {} }; | |
break; | |
case 'filter': | |
newExpression = { | |
filter: { | |
fieldName: '', | |
filterType: GADimensionFilterType.STRING, | |
stringFilter: { matchType: GAStringFilterMatchType.EXACT, value: '', caseSensitive: false } | |
} | |
}; | |
break; | |
default: | |
console.error(`지원하지 않는 표현식 타입입니다: ${option.value}`); | |
return; | |
} | |
onChange(newExpression); | |
}; |
Summary
support dimensions filter and,or,not group
#98
#76
Summary by CodeRabbit
새로운 기능
DimensionFilter
컴포넌트가GAFilterExpressionComponent
로 이름이 변경되고, 필터 표현식을 관리하는 방식이 개선되었습니다.onDelete
및selectedDimensions
가 추가되었습니다.QueryEditorGA4
컴포넌트에서 새로운GAFilterExpressionComponent
를 사용하여 필터 표현식 관리가 개선되었습니다.버그 수정
QueryEditorGA4
컴포넌트에서 필터 표현식 관리 방식이 간소화되었습니다.문서화
GAFilter
인터페이스의filterType
속성이GADimensionFilterType
으로 변경되어 필터의 타입이 더 명확해졌습니다.