Forráskód Böngészése

chore: add checkbox & select option filter tests

appflowy 2 éve
szülő
commit
0d5f0d29d9

+ 61 - 6
frontend/rust-lib/flowy-grid/src/entities/filter_entities/select_option_filter.rs

@@ -1,4 +1,5 @@
-use crate::services::field::select_option::SelectOptionIds;
+#![allow(clippy::needless_collect)]
+use crate::services::field::select_option::{SelectOptionIds, SelectedSelectOptions};
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::ErrorCode;
 use flowy_grid_data_model::revision::GridFilterRevision;
@@ -9,13 +10,34 @@ pub struct GridSelectOptionFilter {
     #[pb(index = 1)]
     pub condition: SelectOptionCondition,
 
-    #[pb(index = 2, one_of)]
-    pub content: Option<String>,
+    #[pb(index = 2)]
+    pub option_ids: Vec<String>,
 }
 
 impl GridSelectOptionFilter {
-    pub fn apply(&self, _ids: &SelectOptionIds) -> bool {
-        false
+    pub fn apply(&self, selected_options: &SelectedSelectOptions) -> bool {
+        let selected_option_ids: Vec<&String> = selected_options.options.iter().map(|option| &option.id).collect();
+        match self.condition {
+            SelectOptionCondition::OptionIs => {
+                let required_options = self
+                    .option_ids
+                    .iter()
+                    .filter(|id| selected_option_ids.contains(id))
+                    .collect::<Vec<_>>();
+                // https://stackoverflow.com/questions/69413164/how-to-fix-this-clippy-warning-needless-collect
+                required_options.is_empty()
+            }
+            SelectOptionCondition::OptionIsNot => {
+                for option_id in selected_option_ids {
+                    if self.option_ids.contains(option_id) {
+                        return true;
+                    }
+                }
+                false
+            }
+            SelectOptionCondition::OptionIsEmpty => selected_option_ids.is_empty(),
+            SelectOptionCondition::OptionIsNotEmpty => !selected_option_ids.is_empty(),
+        }
     }
 }
 
@@ -56,9 +78,42 @@ impl std::convert::TryFrom<u8> for SelectOptionCondition {
 
 impl std::convert::From<Arc<GridFilterRevision>> for GridSelectOptionFilter {
     fn from(rev: Arc<GridFilterRevision>) -> Self {
+        let ids = SelectOptionIds::from(rev.content.clone());
         GridSelectOptionFilter {
             condition: SelectOptionCondition::try_from(rev.condition).unwrap_or(SelectOptionCondition::OptionIs),
-            content: rev.content.clone(),
+            option_ids: ids.into_inner(),
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    #![allow(clippy::all)]
+    use crate::entities::{GridSelectOptionFilter, SelectOptionCondition};
+    use crate::services::field::select_option::{SelectOption, SelectedSelectOptions};
+
+    #[test]
+    fn select_option_filter_is_test() {
+        let option_1 = SelectOption::new("A");
+        let option_2 = SelectOption::new("B");
+
+        let filter_1 = GridSelectOptionFilter {
+            condition: SelectOptionCondition::OptionIs,
+            option_ids: vec![option_1.id.clone(), option_2.id.clone()],
+        };
+
+        assert_eq!(
+            filter_1.apply(&SelectedSelectOptions {
+                options: vec![option_1.clone(), option_2.clone()],
+            }),
+            false
+        );
+
+        assert_eq!(
+            filter_1.apply(&SelectedSelectOptions {
+                options: vec![option_1.clone()],
+            }),
+            true,
+        );
+    }
+}

+ 3 - 1
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -4,6 +4,7 @@ use crate::services::field::select_option::*;
 use crate::services::field::{
     default_type_option_builder_from_type, type_option_builder_from_json_str, DateChangesetParams, DateChangesetPayload,
 };
+use crate::services::row::AnyCellData;
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::revision::FieldRevision;
 use flowy_sync::entities::grid::{FieldChangesetParams, GridSettingChangesetParams};
@@ -364,7 +365,8 @@ pub(crate) async fn get_select_option_handler(
         Some(field_rev) => {
             let cell_rev = editor.get_cell_rev(&params.row_id, &params.field_id).await?;
             let type_option = select_option_operation(&field_rev)?;
-            let option_context = type_option.select_option_cell_data(&cell_rev);
+            let any_cell_data: AnyCellData = cell_rev.try_into()?;
+            let option_context = type_option.selected_select_option(any_cell_data);
             data_result(option_context)
         }
     }

+ 46 - 25
frontend/rust-lib/flowy-grid/src/services/field/select_option.rs

@@ -4,10 +4,9 @@ use crate::services::row::AnyCellData;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::parser::NotEmptyStr;
-use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataEntry};
+use flowy_grid_data_model::revision::{FieldRevision, TypeOptionDataEntry};
 use nanoid::nanoid;
 use serde::{Deserialize, Serialize};
-use std::str::FromStr;
 
 pub const SELECTION_IDS_SEPARATOR: &str = ",";
 
@@ -61,19 +60,17 @@ impl std::default::Default for SelectOptionColor {
     }
 }
 
-pub fn make_select_context_from(cell_rev: &Option<CellRevision>, options: &[SelectOption]) -> Vec<SelectOption> {
-    match cell_rev {
-        None => vec![],
-        Some(cell_rev) => {
-            if let Ok(type_option_cell_data) = AnyCellData::from_str(&cell_rev.data) {
-                select_option_ids(type_option_cell_data.cell_data)
-                    .into_iter()
-                    .flat_map(|option_id| options.iter().find(|option| option.id == option_id).cloned())
-                    .collect()
-            } else {
-                vec![]
-            }
-        }
+pub fn make_selected_select_options<T: TryInto<AnyCellData>>(
+    any_cell_data: T,
+    options: &[SelectOption],
+) -> Vec<SelectOption> {
+    if let Ok(type_option_cell_data) = any_cell_data.try_into() {
+        let ids = SelectOptionIds::from(type_option_cell_data.cell_data);
+        ids.iter()
+            .flat_map(|option_id| options.iter().find(|option| &option.id == option_id).cloned())
+            .collect()
+    } else {
+        vec![]
     }
 }
 
@@ -103,7 +100,7 @@ pub trait SelectOptionOperation: TypeOptionDataEntry + Send + Sync {
         SelectOption::with_color(name, color)
     }
 
-    fn select_option_cell_data(&self, cell_rev: &Option<CellRevision>) -> SelectOptionCellData;
+    fn selected_select_option(&self, any_cell_data: AnyCellData) -> SelectOptionCellData;
 
     fn options(&self) -> &Vec<SelectOption>;
 
@@ -143,22 +140,40 @@ pub fn select_option_color_from_index(index: usize) -> SelectOptionColor {
     }
 }
 pub struct SelectOptionIds(Vec<String>);
+
+impl SelectOptionIds {
+    pub fn into_inner(self) -> Vec<String> {
+        self.0
+    }
+}
+
 impl std::convert::TryFrom<AnyCellData> for SelectOptionIds {
     type Error = FlowyError;
 
     fn try_from(value: AnyCellData) -> Result<Self, Self::Error> {
-        let ids = select_option_ids(value.cell_data);
-        Ok(Self(ids))
+        Ok(Self::from(value.cell_data))
     }
 }
 
 impl std::convert::From<String> for SelectOptionIds {
     fn from(s: String) -> Self {
-        let ids = select_option_ids(s);
+        let ids = s
+            .split(SELECTION_IDS_SEPARATOR)
+            .map(|id| id.to_string())
+            .collect::<Vec<String>>();
         Self(ids)
     }
 }
 
+impl std::convert::From<Option<String>> for SelectOptionIds {
+    fn from(s: Option<String>) -> Self {
+        match s {
+            None => Self { 0: vec![] },
+            Some(s) => Self::from(s),
+        }
+    }
+}
+
 impl std::ops::Deref for SelectOptionIds {
     type Target = Vec<String>;
 
@@ -173,12 +188,6 @@ impl std::ops::DerefMut for SelectOptionIds {
     }
 }
 
-fn select_option_ids(data: String) -> Vec<String> {
-    data.split(SELECTION_IDS_SEPARATOR)
-        .map(|id| id.to_string())
-        .collect::<Vec<String>>()
-}
-
 #[derive(Clone, Debug, Default, ProtoBuf)]
 pub struct SelectOptionCellChangesetPayload {
     #[pb(index = 1)]
@@ -314,3 +323,15 @@ impl TryInto<SelectOptionChangeset> for SelectOptionChangesetPayload {
         })
     }
 }
+
+pub struct SelectedSelectOptions {
+    pub(crate) options: Vec<SelectOption>,
+}
+
+impl std::convert::From<SelectOptionCellData> for SelectedSelectOptions {
+    fn from(data: SelectOptionCellData) -> Self {
+        Self {
+            options: data.select_options,
+        }
+    }
+}

+ 8 - 7
frontend/rust-lib/flowy-grid/src/services/field/type_options/multi_select_type_option.rs

@@ -2,8 +2,8 @@ use crate::entities::{FieldType, GridSelectOptionFilter};
 
 use crate::impl_type_option;
 use crate::services::field::select_option::{
-    make_select_context_from, SelectOption, SelectOptionCellContentChangeset, SelectOptionCellData, SelectOptionIds,
-    SelectOptionOperation, SELECTION_IDS_SEPARATOR,
+    make_selected_select_options, SelectOption, SelectOptionCellContentChangeset, SelectOptionCellData,
+    SelectOptionIds, SelectOptionOperation, SelectedSelectOptions, SELECTION_IDS_SEPARATOR,
 };
 use crate::services::field::type_options::util::get_cell_data;
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
@@ -30,8 +30,8 @@ pub struct MultiSelectTypeOption {
 impl_type_option!(MultiSelectTypeOption, FieldType::MultiSelect);
 
 impl SelectOptionOperation for MultiSelectTypeOption {
-    fn select_option_cell_data(&self, cell_rev: &Option<CellRevision>) -> SelectOptionCellData {
-        let select_options = make_select_context_from(cell_rev, &self.options);
+    fn selected_select_option(&self, any_cell_data: AnyCellData) -> SelectOptionCellData {
+        let select_options = make_selected_select_options(any_cell_data, &self.options);
         SelectOptionCellData {
             options: self.options.clone(),
             select_options,
@@ -47,12 +47,13 @@ impl SelectOptionOperation for MultiSelectTypeOption {
     }
 }
 impl CellFilterOperation<GridSelectOptionFilter> for MultiSelectTypeOption {
-    fn apply_filter(&self, any_cell_data: AnyCellData, _filter: &GridSelectOptionFilter) -> FlowyResult<bool> {
+    fn apply_filter(&self, any_cell_data: AnyCellData, filter: &GridSelectOptionFilter) -> FlowyResult<bool> {
         if !any_cell_data.is_multi_select() {
             return Ok(true);
         }
-        let _ids: SelectOptionIds = any_cell_data.try_into()?;
-        Ok(false)
+
+        let selected_options = SelectedSelectOptions::from(self.selected_select_option(any_cell_data));
+        Ok(filter.apply(&selected_options))
     }
 }
 impl CellDataOperation<String> for MultiSelectTypeOption {

+ 4 - 8
frontend/rust-lib/flowy-grid/src/services/field/type_options/single_select_type_option.rs

@@ -1,11 +1,9 @@
 use crate::entities::{FieldType, GridSelectOptionFilter};
-
 use crate::impl_type_option;
 use crate::services::field::select_option::{
-    make_select_context_from, SelectOption, SelectOptionCellContentChangeset, SelectOptionCellData, SelectOptionIds,
-    SelectOptionOperation,
+    make_selected_select_options, SelectOption, SelectOptionCellContentChangeset, SelectOptionCellData,
+    SelectOptionIds, SelectOptionOperation,
 };
-
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use crate::services::row::{
     AnyCellData, CellContentChangeset, CellDataOperation, CellFilterOperation, DecodedCellData,
@@ -13,9 +11,7 @@ use crate::services::row::{
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::{FlowyError, FlowyResult};
-
 use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
-
 use serde::{Deserialize, Serialize};
 
 // Single select
@@ -30,8 +26,8 @@ pub struct SingleSelectTypeOption {
 impl_type_option!(SingleSelectTypeOption, FieldType::SingleSelect);
 
 impl SelectOptionOperation for SingleSelectTypeOption {
-    fn select_option_cell_data(&self, cell_rev: &Option<CellRevision>) -> SelectOptionCellData {
-        let select_options = make_select_context_from(cell_rev, &self.options);
+    fn selected_select_option(&self, any_cell_data: AnyCellData) -> SelectOptionCellData {
+        let select_options = make_selected_select_options(any_cell_data, &self.options);
         SelectOptionCellData {
             options: self.options.clone(),
             select_options,

+ 19 - 0
frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs

@@ -83,6 +83,25 @@ impl std::convert::TryFrom<&CellRevision> for AnyCellData {
     }
 }
 
+impl std::convert::TryFrom<&Option<CellRevision>> for AnyCellData {
+    type Error = FlowyError;
+
+    fn try_from(value: &Option<CellRevision>) -> Result<Self, Self::Error> {
+        match value {
+            None => Err(FlowyError::invalid_data().context("Expected CellRevision, but receive None")),
+            Some(cell_rev) => AnyCellData::try_from(cell_rev),
+        }
+    }
+}
+
+impl std::convert::TryFrom<Option<CellRevision>> for AnyCellData {
+    type Error = FlowyError;
+
+    fn try_from(value: Option<CellRevision>) -> Result<Self, Self::Error> {
+        Self::try_from(&value)
+    }
+}
+
 impl AnyCellData {
     pub fn new(content: String, field_type: FieldType) -> Self {
         AnyCellData {